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.”
]]>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!
]]>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.
]]>Dinosaur golf before it shuts for the season.
]]>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.
]]>Solar powered.
]]>I had time to take a walk before running some errands and getting a flu shot and a Covid booster this morning.
]]>It is starting to smell like fall!
]]>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!
]]>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!
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 APL’s 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 APL’s 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]]
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]]
3 3 ⍴ 1 2 3
Returns
1 2 3
1 2 3
1 2 3
]]>
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.
]]>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.
]]>Home beach. Last day of vacation for me.
]]>I really like the unpredictable depth of field and color handling of single-use cameras. The day before we left for a little vacation to down east Maine I wrote another weird little camera app, lut cam. Lut cam attempts to simulate some of the aspects of a single-use camera by allowing you to apply color profiles to the raw image produced by a device’s camera.
I now have a collection of weird vacation photos that sorta look like they came from 1994.
Because I did this using JavaScript and browser APIs, I didn’t have guaranteed access to the device’s focusing mechanisms which means the images are pretty much whatever the sensor picks up — distorted lens, graininess, and wonky focus included. It was a fun follow up project to my dithering stuff because it really makes apparent how much computational work phone cameras do to make up for their physically diminutive optics.
All my recent projects have been made within pretty short periods of time — typically a weekend, or, in the case of lut cam a few hours…I enjoy working under those sorts of constraints because it encourages me to work off of previous work rather than jump down new deep, dark, rabbit holes where I’ll likely get lost. A feature I’d like to add to both pico cam, and lut cam is a level — I think I can use the device orientation and device motion events to accomplish this, but haven’t tried, yet…because we were on vacation, which was lovely!
We visited our old stomping grounds, met up with fiends friends, made new ones, and spent almost enough time on the best rope swing this side of the Atlantic. It was a good way to send up the summer!
Summer vibing, fog horn whooping.
]]>Rocky shore.
]]>We took a boat to a teeny tiny island called Bean Island. We walked around the entire edge of the island’s rocky coastline.
There were some gloriously regal looking black backed gulls along the shore, as well as a pair of juvenile bald eagles who were making hilariously squeaky noises.
]]>In my last post I said that
I’ve had a few ideas for other personal experiments I wanna build on those walks, but haven’t actually wanted to do much programming — maybe this fall or winter will be a good time for that?
Welp, it wasn’t even an idea when I wrote that, but I made another implementation of pico cam, this time using swift for iOS. I won’t release it to the App Store because I don’t wanna give Apple that money, but it is available on GitHub, so if you know how, it is pretty workable!
It was a fun project and I learned a bit a more about SwiftUI. I haven’t touched SwiftUI pretty much since it was first released. Being thoroughly web-dev-brained, it is pretty approachable, as long as you don’t stray from the happy path. When I worked somewhere that did a lot of app development, the first iterations of an app tended to have wicked rough UI, which lead to some really bad UX — we usually had to do a lot of design clean up. SwiftUI seems to directly address that; as long as you stay within the rails offered by SwiftUI you get a lot of good UI, with mostly solid UX, for “free.”
This project rekindled my belief that someone could relatively quickly churn out very small, focused apps that help to address a single, or small set of well-defined needs. I forget who said it, but I recently saw someone on the fediverse talk about hobbit-core apps — I think maybe this is that? I think that it would be dreamy to work on tooling like that — where, rather than having to be concerned with growth and adoption, and what a feature roadmap with a hilariously long tail (8 years!?), I could help address the known needs of a known group of folks with small, easy to release applications.
]]>This summer my oldest kid — 8 years old — asked to learn more about programming. They’ve already got about a full time job’s worth of experience with Minecraft’s red stone, Super Mario Maker 2, Logo, and Scratch so I knew we weren’t starting from nil, but, despite having done a bit of teaching about programming with kids in the past, I hemmed and hawed. After hemming and hawing for a bit, though, I realized that I was hemming and hawing about the totally wrong thing! I was caught up in the whirlpool of pros and cons and learnability of various programming languages…once free of that we set down a path:
There wasn’t a particular goal — no one said “I’d like to make X so that I can do Y” or anything like that. The goal was “to learn more about programming.” With that, we went on a few walks together where we talked at a high level about how computers work, and what they’re good at — e.g. they don’t think, but they are very good at following instructions in sequence. Then, back home we sat at the computer and worked through some key constructs, first seeing the well-known-to-us version from Scratch and then seeing the equivalent structure in Racket and Lua. DrRacket and Tic-80 were both really useful, low-ceremony tools for those explorations.
Next, we moved to solving little puzzles. The shape language of maria.cloud (also present in Racket) was a lot of fun and paired well with puzzles from the APL challenge.
With that, I think we’ve reached a good spot to hang at for a while.
Beyond those explorations, the summer continues to progress fairly agreeably. Work has been mid to frustrating, which isn’t honestly anything to complain about in that I still have a job in an industry that is seemingly self immolating right now. The family and I have been enjoying Maine beach adventures, and biking about. I’ve fallen into the habit of taking the dog for long walks in the evenings after everyone else is in bed. I’ve had a few ideas for other personal experiments I wanna build on those walks, but haven’t actually wanted to do much programming — maybe this fall or winter will be a good time for that?
I recently finished reading Radiance by Catherynne M. Valente. It was an interesting contrast to Jonathan Strange & Mr. Norrell by Susanna Clarke which I read earlier this year. Am…am I a historical fiction kinda creature all of a sudden!? Who am I kidding, I’ve always been like this.
…onward from that: I’ve been playing a wee bit of a game called Arranger. It is a tactile sliding puzzle game. I’m certain reviews will say it is too easy, but I am really enjoying the puzzling, the narrative is cute so far and the vibes are pretty immaculate for a chill evening game before bed. It also has a really really clear objective log that makes it easy to pick back up after some time away without having to look around for a long while trying to sort out what you were working to accomplish. 10 out of 10, I believe that all games should have this sort of thing for lame-ohs like me who don’t play games for more than an hour or so at a time sporadically during a week, or throughout the course of a month. The game also has my favorite feature of any game — the ability to crank the text size up to an absolutely absurd size.
Here is some silly APL I wrote at work today. I needed a quick way to create some files that had exactly 300,000 characters in them with no spaces.
a←300000⍴0
s←,⍕a
ns←(s ≠ ' ') / s
⎕←ns
And with that, I’m 100% adding APL to my resume because I’ve honestly written some for work.
See the code go brrrrrr. And, for extra credit, I re-wrote it to be (slightly) more terse. I need more practice code golfing with APL. I should be doing something with a right or left tack so that I don’t have to repeat the statement with a rho that creates the array of 300,000 zeros.
]]>“It’s hard for a hungry man to swager”
This line from The Tale of Thorleif Earl’s Poet has gotta be one of the greatest lines from all of Icelandic saga literature.
]]>After having dithering-fun making dither it and pico cam I made a little game, currently called “puzzle dungeon,” which I admit isn’t a very good name at all. Puzzle dungeon is part logo, part dungeon crawling rogue-like and it doesn’t have anything whatsoever to do with dithering.
Otherwise on things computer, I’m back to learning about logic programming — specifically prolog, mini kanren and picat. I really like them, and, like functional programming and array programming I’ve found that logic programming has quickly poisoned my thinking when I go to solve problems in more general purpose sorts of situations.
I updated the projects section of my wiki. Stuff is a wee bit more organized there, now. Things are organized into 3 categories:
…and a secret fourth category of miscellaneous other stuff.
Beyond those computer things, summer is in full swing! I’ve been reading a lot of fantasy. I think I’ll turn back to sci-fi, soon. I’ve also been watching a bit of anime. I’m trying to fill a Frieren shaped hole in my heart. No dice so far, though I’m a few episodes in to a show called Ranking of Kings. I have mixed feelings about it that I won’t go into at length, but the tl;dr of it is that it is telling a story that centers disability in a few forms…and sometimes I think it lands it, and other times I think it very much doesn’t.
]]>Frog search.
]]>This weekend we traveled 20 minutes to a sort of secret beach. It was a grey, overcast day, and we timed our trip to line up with low tide so that we could walk waaaaaaay far out into the ocean all the way to some little islands. It was fun, and we saw some neat birds, including an Oyster Catcher. While on this adventure I took a picture. Later at home I thought “it’d be nice to dither this!” I usually reach for Dither me this for such tasks, but for some reason I tried to do it from the command line. That quickly became frustrating. Not because it didn’t work but because it was wickedly clunkier than I remembered it being…so I got to thinking, “maybe I can make my own tool to do this!?”
A bit of noodling, a pile of JavaScript and a few hours later I was done! Dither it is a small website where you can select an image from your local device and it’ll automatically be transformed using a Floyd-Steinberg dithering algorithm. Then you can save it. Nothing ever leaves your browser, no settings or knobs to fiddle with (for both good and ill).
I expected the dithering algorithm to be the most difficult bit. It wasn’t because it was pretty easy to find a whole lot of great example implementations. In the end the most difficult bit I ran into was figuring out how to turn the contents of an html canvas into a downloadable image file. Once that nut was cracked it all seemed to be done and dusted…
…until then some time passed and I began to wonder “why am I processing images, why not take dithered photos to start with!?”
I slid down a rabbit hole researching the Gameboy camera and noodling if I should get a used one to use with an Analogue Pocket?
That seems like a wild setup to carry around and like it takes a bit of planning to effectively use. I’m not much of a planner. You know what doesn’t involve any planning for me? Taking photos with my phone! It is honestly the only camera I use.
Enter a new project! Pico cam is a small web application that lets you take dithered photos. The images it produces are the same dimensions as the Gameboy camera’s. It was an interesting project, and I’m pleased with the results. I expected this project to be more challenging than it ended up being. I quickly learned that contemporary browsers can do a lot of the heavy lifting for you, so most of what I assumed would be difficult I was able to handle using in-built browser APIs. As is my habit, I was able to pull this project off using only JavaScript in the browser, no external packages, or build step or anything like that.
My approach was to first dump a live stream from a connected camera into a <video>
tag, then to sort of “stream” frames from that video to a <canvas>
element, at which point I’m essentially running the code from Dither it! Easy! Next up was a button to save individual frames from the canvas to the device and bip, bop, boop, a bit of css and markup re-arranging I have a totally passable Gameboy camera web app!
If you end up using either Dither it or Pico cam I’d love to see the pictures you make!
]]>Or at least the story of life as I implemented it in swift recently as a little learning project because I haven’t written any swift since walking away from mobile dev a few years ago (no regrets)!
First there was the universe! Well, first there was some requisite boilerplate, but then there was the universe! A 2 dimensional grid, an array of 10 columns and 10 rows.
import Foundation
let rows: Int = 10
let cols: Int = 10
Into that great void will be brought life.
The universe and life hold to simple truths:
func countLiveNeighbors(_ board: [[Int]], x: Int, y: Int) -> Int {
// All of the possible directions to check for live neighbors
let directions: [(Int, Int)] = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
// Iterate over the directions and count live neighbors
return directions.reduce(0) { count, dir in
// Calculate the coordinates of the neighbor cell
let newX: Int = x + dir.0
let newY: Int = y + dir.1
// Check if the neighbor cell is within the bounds of the board
if newX >= 0 && newX < rows && newY >= 0 && newY < cols {
// If the neighbor cell is within bounds, add its value to the count
return count + board[newX][newY]
}
// If the neighbor cell is out of bounds, ignore it and continue with the next direction
return count
}
}
func nextGeneration(_ currentBoard: [[Int]]) -> [[Int]] {
// Iterate over each cell in the current board
return currentBoard.enumerated().map { (x: Int, row: [Int]) in
return row.enumerated().map { (y: Int, cell: Int) in
// Count the number of live neighbors for the current cell
let liveNeighbors: Int = countLiveNeighbors(currentBoard, x: x, y: y)
// Determine the next state of the cell
if cell == 1 && (liveNeighbors < 2 || liveNeighbors > 3) {
// Determine death
return 0
} else if cell == 0 && liveNeighbors == 3 {
// Determine life
return 1
}
// Do nothing
return cell
}
}
}
A clock to tick and a weaver to draw our life on to the cosmos for us to behold.
func printBoard(_ board: [[Int]]) {
board.forEach { row in
row.forEach { cell in
print(cell == 1 ? "@" : ".", terminator: "")
}
print()
}
print("\n")
}
With our mechanisms for life and death and viewing in place we gaze into the cosmos and we say to Chronos: “begin.”
func simulate(board: [[Int]], generations: Int) {
var currentBoard: [[Int]] = board
for _ in 0..<generations {
printBoard(currentBoard)
currentBoard = nextGeneration(currentBoard)
sleep(1)
}
}
func randomBoard() -> [[Int]] {
return (0..<rows).map { _ in
return (0..<cols).map { _ in
return Int.random(in: 0...1)
}
}
}
simulate(board: randomBoard(), generations: 10)
…and it works!
But, of course, don’t miss the most elegant implementation of life ever there was.
Here is the full implementation for any of ya’ll sickos who want your own copy. No license. No promises. No documentation. No rulers. Go nuts.
]]>In the span of two posts here I’ve witnessed two astronomical events! First the total solar eclipse, and then more recently the aurora borealis. Both were amazing, especially the aurora. I’d never seen either before and both were pretty incredible to behold.
I left my old job at the start of the month, and, after a few days off, I’ve started a new one! I’m really excited for this one, though around day two the anxiety and imposter syndrome started to kick in pretty hard core. But, I’m coming in to a senior position, in exactly my sorta jam, so I know if I give it some time I’ll find my sea legs pretty quickly.
With a few days off, I found myself with more time than usual to play video games. Two stand out titles include Rusty’s Retirement and Carto. Rusty’s Retirement is an idle farming sim where you and a pack of pixel-art robots set up a little farm. The neatest part of the game is that it doesn’t include any of the punishment mechanics from other farming sims. If you walk away or don’t play for a while nothing bad happens. Crops don’t languish, no virtual people starve. Reserves aren’t depleted. Alarms don’t sound. If you keep it running the robots finish up their tasks, then sit on benches or otherwise chill. I felt motivated to make them cozy little benches surrounded by flowers and mushrooms. The second neatest part of the game is that you cannot play it in full screen. It docks to one side of your screen, the idea being that you can keep it running while you do other stuff on your computer. It is sort of like a more involved Tamagotchi.
Carto is a paper-feel adventure/puzzle game. I’ve only played through the first few minutes of it, but so far am enjoying it and am excited to play more. When I started it I wasn’t expecting it to have as much story as it does.
My other gaming accomplishment was that, after literally years of trying I got through the first section of Celeste. 🍓
On the reading front I’ve been reading Babel, or the Necessity of Violence: an Arcane History of the Oxford Translators’ Revolution, by R. F. Kuang. I’m enjoying it, but finding it a bit heavy handed (which may well be the point). It pairs unexpectedly nicely with The Mushroom at the End of the World: On the Possibility of Life in Capitalist Ruins by Anna Tsing since both are in conversation with colonialism, capitalism, and what I wanna label as “placedness.” Not sure what I’ll be reading next.
A closing link: I’ve been really enjoying Ivan Reese’s Tone Dome. It is a lot of things, but for the sake of brevity I’ll describe it as an ambient soundscape generator. It gets real wild if you run it on a couple devices all at once.
]]>