Game Pitch: AstroRush

Good Morning from my Robotics Lab! This is Shadow_8472, and today I have a game I’ve started making with a side of working on my homelab. Let’s get started.

The Pitch

AstroRush

A re-imagining of the classic asteroid-blasting arcade staple, this simple 3D space shooter puts you in the cockpit of a nimble spacecraft pulverizing asteroids in search of their mineral wealth. But is humanity alone in its interest in the Alpha Centauri system?

Styled to resemble vector graphics of the inspirational Atari title, Asteroids, AstroRush is my first game in my lineup to build my skills toward expressing my Container Analogy in an interactive format.

Progress so far

First off, I’m building this game on Redot 26.1, a free and open source game engine forked from Godot to provide a more stable base. The project recently moved from a “soft fork” to a “hard fork” – meaning that backwards compatibility with Godot is no longer a concern. The word I heard is that Godot introduced breaking changes in a minor update, which generally is a big no-no.

Moving on to AstroRush. It’s a working title I came up with to replace “Asteroids 3D” for this post – a pun off of “asteroid” and “gold rush.” The story I have in the back of my mind is that humanity is building its first inter-stellar colony, and all precious minerals are required.

Development proper started by grabbing my free flight controller and a picture of the sky from NASA for my background. I got the composite of the starry sky to feel like it could be drawn by an arcade game by turning down the saturation and maxing out contrast in the environment node.

And then most of my time was spent on dead ends trying to get the look just right. This category is split into two sections: first is shaping the asteroids, the second is outlining models with all-black albedo. To shape the asteroids, I first made a vertex shader to nudge around the points of an Ico Sphere out of Blender. The naive approach of displacing each vertex gave me a “nacho explosion” where each triangle goes off on its own. The trick was using the values at a 3D noise texture as a seed. But I found I could only make one unique asteroid using this method.

My second approach was to build the same logic in code. It took a bit longer, but again found that the last mesh defined was being used for everything. This is particularly problematic, as I need meshes to scale for larger/smaller asteroids. I ran my code past the Redot community and learned the engine was working as intended; I had made erroneous assumptions from the documentation. At this point, I’m likely to just “bake” a series of meshes that can be assigned randomly as needed.

Extrapolating the visual style of Asteroids to 3D was probably where I wasted most of my time. After trying and failing to write an outline shader for myself, trying to work with the stencil buffer (turns out it’s listed as experimental, and subject to change), and searching for a pre-made outline, I circled around to one that worked sort of OK, but didn’t particularly like low-polygon games as I was working with.

I wanted to understand the structure of the shader I was working with, so I spent a day re-building it as a visual shader – translating each line of shader code into a form resembling a flow chart. For some reason, I was never able to get it to behave the same as the original, even when I rigged the relevant parts into an “expression” node made for handling code. Yeah… those appear a little buggy at the moment.

Around this time, I was playing with a widgit in the Redot interface when I noticed a pattern in the misbehavior with the outlining. All I had to do was vertex deform the outline mesh as well.

Working on the free flight controller, I found speed caps and drag for both rotation and lateral movement helped with the feeling of a responsive space ship. Without them, turns are a fight against velocity. Think: Kerbal Space Program. I’m also working on making the script more friendly towards using it in other games. That means extending it using a second file.

Takeaway

It will take time, and progress will be incremental as I learn the basics. I still don’t have a playable prototype. Is the game fun? I don’t know. I sure hope so. But as the code base grows, I’m going to want a local Git server in my homelab.

I am thankful for the Redot community being so chill because the Godot Discord has reportedly gone on a witch hunt – banning people over common opinions the moderation team considers hateful – be they expressed openly, in a profile you have to hunt for, or in a leaked DM. No mercy, no notification – just a ban, according to some Redot users I met. Assuming it’s all true, I have to say it’s sad. On the one hand, I believe they hurting themselves. On the other, Freedom of speech is not freedom of platform. They have every legal right to build an echo chamber, but I find it morality dubious at best to eject people for wrongthink when they are otherwise behaving.

Final Question

What would you call this game?

The Importance of Team Communication

Good Morning from my Robotics Lab! This is Shadow_8472 and this month, I participated in the 2026 Redot Winter Game Jam. I have a story out of it, but it didn’t go the best. Let’s get started!

Disclaimer

I do not wish to defame anyone – only warn about the importance of communication. I am therefore substituting the names of my teammates for Stardew Valley characters based loosely on roll in the project while ignoring other characteristics, such as age or gender.

People have bad days, and I am only one witness of a narrow window of time. So please: unless you are a moderator, leave idle curiosity here. Fellow participants, do not allow anything you read here to affect how you rate the entries.

Out the Door Studios

One of my longer-term goals has been to code a big idea I’ve had as a video game, but I need to build skills to that end first. I entered the Redot Winter Game Jam last year to do just that. Redot is a fork of Godot – itself a free and open source game engine. Godot made a comment I found unwelcoming on their socials, and Redot prioritizes making games over witch hunting wrongthinkers. Under that context, I built what became Out the Door Studios. Going into the Redot Winter 2026 jam, I have two jams worth of experience plus a little from personal projects.

A Group Founded in Poor Communication

The official theme for this jam is officially “Slippery when…”. For me, it is miscommunication – not all of it mine, not all of it bad. The first miscommunication involved meeting Sebastian, a programmer with a little 3D experience. He mistook our 2025 entry for 2026, but I invited him to Out the Door nonetheless. Nobody I’ve previously jammed with was both available and interested. Within a few minutes of my pre-jam pitch, he had delivered a visual matching my inspiration perfectly.

And then I spotted an ad by Lewis. He wanted a programmer and a 3D artist to round out his jam team for a more ambitious 3D project. I applied, offering Sebastian’s and my talents. It went back and forth between Sebastian, myself, and Lewis for quite a while, with me making Lewis out to be “middle-to-top of the little leagues.” Somewhere in there, I listed my years of experience with Linux intending that as an ability to solve problems.

Unbeknownst to me at the time, Lewis was more “bottom of the big leagues” and took my experience maintaining a homelab to mean I was programming the entire time. When I arrived in Lewis’ Discord, it had about 10x the members I expected. It is set up more like an office than an informal development environment. Impostor syndrome set in, but I pushed it aside. Their rules weren’t copy-pasted –usually a good sign– but the casual coarse language made me uncomfortable from the start.

Already on the team were people to whom I will refer as Leah, a concept artist I didn’t interact with much, and Maru, another programmer. Yellow flag out the gate: time zones – the five of us were spread roughly equally around the globe. Most pointedly: our programmers shared little in the way of sleep schedule, severely hampering collaboration.

The Jam Begins

Now, I never signed any NDA’s (Non-Disclosure Agreements), but as of publication, the jam is ongoing; I will avoid mentioning specifics unless they’re vague or important. We got a relatively good jam theme, “Slippery when…”, lending itself as easily to ice/slipping as stealth/evasion/pursuit. Four of us hopped in voice (Maru was asleep).

In that first meeting, I immediately got cussed at to turn my mic down (apparently, my mic settings were out of whack from fiddling last month). I proposed using Codeberg – having to explain the difference between GitHub (code-hosting website) and Git (version control software at GitHub’s heart), but Lewis about insisted on using GitHub, “let’s just stick with what’s familiar.” ‘But, GitHub is not familiar to me,’ I thought. His house, his rules.

Alone, that wouldn’t mean much, but every design decision followed suit. At one point, we were split 3:2 over what to call a gameplay element. Despite Lewis landing in the minority, it shifted from “we’ll just agree to know what we mean when either term comes up” to an unspoken, ‘just use Lewis’ term in-code.’ Yellow flag.

My main contribution to the game was locating a system important to the main gameplay gimmick. I found something on the in-engine asset store and had it half-way working, but after a day it proved buggy and inherently didn’t support browsers. I spent another day studying how similar effects were accomplished in familiar titles. One lonely video demonstrated everything we needed running in Godot (which Redot is compatible with), but without code or contact info.

The Tutorial

Then someone –Sebastian, I believe– located the tutorial. How to do <our gimmick> in Godot 4. Code was published, but lacked a visible license. I spent a day tracking one down. Let’s call the tutorial author Mr. G4 for Godot 4. His project built off someone’s Godot 3 code. Mr. G3 was following up on a fourth party’s Godot 2 demo. Between them, I only found an eMail for Mr. G3. Mr. G2’s digital footprints ended in 2021, one post after retweeting an incitement to violence against police in 2020, which I reported. Mr. G3 got back to me with an MIT License, and I was able to share my Container Analogy post.

Mr. G4 was tricky. I asked Lewis, citing enough context between the video and comments section to treat it as MIT, and he said his personal philosophy is that if there’s no license, just do whatever; it’s just a game jam. Yellow flag: one might get away with CC0 (public domain, even without public domain), but that’s not how copyright usually works.

I showed the tutorial to Muggyfox1, a colleague from my second jam, who figured it was fine. The code had been online for years, I made an effort to reach out, and the author clearly intended to be copy-pasted.

In either Mr. G4’s implementation or the asset library’s, the mechanic relies on a shader (GPU code usually used for graphics). Maru was wary of using shaders, but I figured it was within reach, given ready code and my success reading data back out of the graphics card.

Meeting

Sunday came around, and the jam team was invited to a meeting. Sebastian had a previous engagement, and I woke up early. Did I say the jam team? No, it was server-wide! I caught the tail end of a foul mouthed, pre-meeting goof off session. They deliberated content creator sponsorship programs to apply for as a server while alluding to male reproductive organs at the pair they eventually got.

I abstained from deliberation and voting, as I was planning to peace out after the jam at least until another jam – besides, none of the potential sponsors interested me anyway. When pressed for comment on something, I noted for the record that I considered myself as “on probation” for the time being.

Flashpoint and Aftermath

A week into the two-week jam, Lewis was feeling the time crunch. He was using all-caps in chat, demanding that someone start the 3D environment. We programmers were presumably all waiting on each other for things. Lacking a good 3D environment, I had been asking for help all day to add a slightly less important feature to the shader. Eventually, I entered a voice call with Lewis.

Lewis tried the demo I had on GitHub, but it was from a weird development point where something wasn’t working right. I offered to share directly from my screen, but Lewis wasn’t as impressed as I would have hoped. He was quite miffed to hear I was working on a shader and simultaneously told me to “SCRAP IT!!” and, “See it through!” If you’ve ever read about the development of Sonic X-treme, that’s what it felt like.

“Tell me ONE good reason I should not kick you out right now!” he said.

I had already committed to forgiving him after He apologized, otherwise it might have turned into a shouting match. I prayed to Jesus for the situation as I actively ignored any rash words of passion that lent themselves to being hurled back. “I’m the only one who knows how the <REDACTED> system works,” I said as calmly as I could.

I tried not to flake afterwords, but the damage had already been done when Lewis yelled at me. It came into focus the next day after two unproductive hours without positive directions and a reality check with my sister and mother. I was easily spending 10+ hours a day focused on this project, trying to keep things moving when Lewis was offline and people (mostly Sebastian) were at a loss as to what was currently needed. When I DM’ed Maru, she shared her doubts from the start and said, “learning while trying to deliver a complete game in 1 week [sic] is nuts.” (The jam is two weeks.) I had tried to be clear to Lewis, “I am here to learn and have fun with the goal of getting something out the door that is a bit more complex than my previous work.” Neither of those bolded conditions were true anymore.

Sebastian had been more optimistic the night before. I had taught him the system I’d assembled and documented it with plenty of screenshots. He commented on a couple missing features on the shader, and let no one truthfully say I didn’t give it my all to add them until I was burned out.

But when I did burn out, I tried to leave with as much class as possible while still saying things I felt needed to be said. I put my code in order, leaving a new comment or two, then organized the shader with frames around nodes meant to function together. When I told Sebastian about my plans to leave and Maru’s pessimism, he replied, “I might be the only [guy] who can stitch everything together.” Maru expressed disappointment upon hearing we wouldn’t get to work together on something more serious. In the end, I wrote a 500 word letter of resignation telling this story and dropped it in his server, CC’ing everyone in the jam’s section, including what looked like a server admin.

As a last order of business, I licensed my contributions under GPL to Lewis and his team, then ensured its admittance into the main branch (I don’t know that Lewis knows how to use ‘git checkout’ to cleanly remove my contributions). Since I’m not trying to sabotage the jam, I wrote in an exemption until the submission deadline. After that GPL applies, meaning if Lewis wants to continue development, he will have to either publish his entire project’s source as GPL or contact me through a 3rd party for negotiations, but only in the presence of a chaperon approved by me and who has authority to end the conversation at any time for any reason.

Takeaway

I am proud of the work I did complete, and I really hope the team can publish a fun game on time. But at no point did I ever sign any NDA (Non-Disclosure Agreements) nor did I sign away any rights to code I wrote/modified. I hope Lewis grows as an individual from losing me like this. When you work with volunteers, you’re working with volunteers.

I had fun with the mechanic I did get working, and I think I want to do something with it in the future.

Final Question

Have you ever had to walk out on a bad team situation?

I’m Learning Godot

Good Morning from my Robotics Lab! This is Shadow_8472 and today I am learning a thing or two about game development with the Godot game engine. Let’s get started!

Why Godot?

Godot has had my interest for several years. As far as I can tell, it is the only general purpose free and open source game engine (if you want to make a block game for the fun of it, use MineTest instead). Engines like Unreal and Unity allow you access to their software free of charge until you make it big enough that it’s worth their bother going after you. With Godot, your final product is 100% yours, but as with other open source projects, you are encouraged to donate as you are able.

I Want to Make a Sonic Fan Game

My sister (Tzarina8472) and I were on a dog walk when we decided a game about Cream the Rabbit in a chao garden would be fun. The adorable and ever-popular chao –per fan consensus– have gone criminally underused since they were featured in Sonic Adventures 1 and 2 (SA1, SA2) (and their Director’s Cut remasters), where player characters could raise a number of these creatures in a number of themed gardens. Once your chao is strong enough, you can enter it to compete in chao races. While our hypothetical game revolved more around what a wild chao garden would look like, it still made me want to play it for myself.

I have been admittedly lacking in access to primary sources for this project. I only own Sonic Adventure 2: Battle, but both my GameCube and Wii stopped working before I made it half way through the game’s content. We picked up a used Wii in good condition for cheam on short notice on Thanksgiving (Thank you, Jesus!), so that will be on my to-do list some time. Otherwise, I’ve been playing a humble offering I found in the Android Play Store library called PocketChaoGarden.

“Installing” Godot

Godot proper doesn’t require installation. Download/unzip it, and you can run it directly from your file system. The stable version is Godot 3.5.1, but Godot 4 is in beta. Long story short, between my two daily drivers, only DerpyChips is willing to run the stable version. I kept getting any number of errors I’m clueless about for other configurations. I will be adding this and a selection of other of my open issues to my Discord server.

Learning Around Tutorials

I have found the GodotQuest brand tutorials to my liking. They have two introductory course lines – one for game developers switching to Godot, and one for programming newbies. I already know about variables, loops, and data structures, but I bet on the non-elementary course line skipping over vital information about how game engines work in general; I opted to review.

I focused on the playlist Getting started with the Godot game engine in 2021[1]. It introduces concepts one short video at a time. Most importantly: Games are made up of scenes. Scenes are made up of nodes. Scenes can act like nodes for building larger scenes (think: a player scene or enemy scene being put into a level scene). One script can be attached to each node. GDScript is a Python-like scripting language that’s been optimized for use in Godot. The playlist concludes with a pair of two hour long videos where it walks you through assembling a creep crushing game – once in 2D, and again in 3D.

Our Game So Far

I skipped the 2D game and went wildly off script on the 3D one. Where the tutorial provided a textured model, I spent an hour or so making a poorly textured cube in Blender with a sprite of Cream’s face as provided by Taz. I coded the horizontal movement, but I diverged from the tutorial around half an hour in.

A lot of my improvements since splitting with the tutorial have been invisible to what would be an end-user. I spent around day figuring out how to use materials effectively so I can replace art assets without the need for Blender. I reorganized the internal node structure and am presently playing with getting inherited scenes to behave.

Takeaway

This post needed longer than a week to study. I’m learning a lot, but if I gave any more through coverage, I’d find myself parroting the tutorials.

Final Question

Have you ever wanted to make your own game?

I look forward hearing your answers on in the comments below or on my Socials.

Works Cited

[1] GDQuest, “Getting started with the Godot game engine in 2021,” youtube.com, Apr. 22, 2021. [Online]. Available: https://youtube.com/playlist?list=PLhqJJNjsQ7KEcm-iYJ2a8UCRN62bTneKa. [Accessed Nov. 28, 2022].

I Upgraded My Python Discord Bot (Week5)

Good Morning from my Robotics Lab! This is Shadow_8472 and today, the month of bot development concludes. Be sure to catch the previous posts before reading this one. Let’s get started!

Measures of Progress

Progress this week towards the bot while substantial, wasn’t anything special. In fact, the last day I had to work on it was mostly a flop of me goofing off on other pursuits instead of focusing. Instead, I’d like to focus on what I’ve accomplished over the month.

This longer push on the bot built upon a dice bot I programmed up several months ago that understood how to roll X dice with N sides. Over the month, I achieved multiple personal firsts and worked on my coding style. It was always satisfying when I came up with a new or clever trick to reduce code complexity for a given job. I learned a thing or two about coding the “right” way and practiced with Git version control software.

Memorable Milestones and Personal Firsts

The first big milestone that sticks out in retrospect was rewriting the dice rolling engine from scratch. I will likely want to make another dice bot geared for another game in the future. By splitting out related functions of a larger project into separate files, I can almost trivially re-use the code in another project or re-implement the whole thing. Shorter source files are also much easier to navigate and maintain.

Code condensation is fun. It hurts to see a piece of code you spent all day hacking together (successfully) to be replaced in five minutes by a stupidly more efficient approach, but simpler is easier to maintain. I found myself going through my code more than a few times as I learned about the likes of fstrings and the enumerate function to make sure they were being used when they made more sense than old standards or contextually strayed code, respectively.

This week in particular, I noticed how my relationship with exceptions has changed over the long term. As a beginning programmer in high school, exceptions and tracebacks always meant the existence of a bug. As I became more experienced (one of my early blog projects), I encountered a programming problem where the only way I could solve it was to try/catch an exception. On this project, I have a few places where I reached for try/except almost as though it were an if/else statement. I’ve even made use of Python’s ability to manage different kinds of exceptions with different code blocks in a new feature I worked on this week (but haven’t finished as of writing).

Saving and Loading Data

I have next to zero practical knowledge of writing programs with save data. I was able to bodge a webcam subtitle program involving a second webcam and writing text to a file, but that’s it from before this week.

Fancy dice bots have the ability to store information about players and characters involved in a game. My group don’t strictly need this feature to play, but it is high up on the nice-to-have list. Plus this is a skill I am after; it was just so intimidatingly long to research! Modules like Pickle and Shelve that dump data directly into a file warn in their documentation that clever users can inject arbitrary code through clever wording if the programmer isn’t careful. I started with a module called “configParser,” before realizing this wasn’t exactly its intended use case. It’s close enough though, but it still took a while to gain a mental model of how it operates internally – with an object in RAM, and not directly on the disk. Write the object to essentially a plaintext file, and it can be read back fairly simply. As an important bonus, the saved files are human readable.

Again, saving data to files is perfectly safe. It’s important though to be careful when loading naively stored user-generated data. While researching, I learned about a 15 year-old, trivially exploited vulnerability in the tarfile module that circulated in the news last week. The moral: know what you’re loading, and sanitize incoming data from users or directly from the Internet before saving it.

Takeaway

Sometimes, you need to look at something ten or more times before things start making more sense than not. Programming takes time to learn. One new technique I tried out this week (but wasn’t able to use) is called Ternary Operators. These look like if/else statements nestled into a single line, but they’re more for when you have multiple variations on a single command. My to-do list on this project is quite lengthy as I still need to diagnose why my newest feature collapsed on me. I wish I could end the month with everything in a working state, but such as it is, I have other things I need to work on too.

Final Question

What is your most ambitious personal project to date?

I Upgraded My Python Discord Bot (Week4)

Good Morning from my Robotics Lab! This is Shadow_8472 and today, the month of bot development continues. Be sure to catch the previous posts before reading this one. Let’s get started!

Iteration vs. Recursion

My first major milestone this week was coding a recursive dice tally function to replace the iterative one. Iteration and recursion are two approaches to solving the same types of problems.

The experience of iteration is the more native one to think in. Your program has a path to walk that loops back in on itself as needed. Its downside is that this path can grow beyond the comprehension of the programmer fairly quickly when using in loops within loops within loops if need be. From experience, I found myself guessing more often than not when I was finishing the iterative tally function, and even then I had to teach myself to indent debug text.

To get the idea of recursion, imagine having an army of clones. Each recursion clone has a tiny job, but half that job is working out what it’s supposed to do, and most of the time, that it’s subdividing the problem and summoning one or more clones to work on each one. Recursion has a higher learning curve, but it’s a lot simpler to maintain. I was able to identify and squash a bug involving my pip tally missing secondary pips within seconds to a minute as opposed to fixing the same bug in minutes to an hour and a lot print() statements.

Code Polishing

The more time I spend on this project, the more I discover or rediscover tidier ways to code. It’s not always a hundred lines crunched down into two or three, but it can still feel validating to get a whole chunk of debug code onto a single line I can easily comment out.

In terms of memorable condensations this week, I had two functions –cleanInput() and tokenizeInput() (the later of which underwent around an eighteen:three line reduction last week)– in my dice engine file used for turning raw input into a format my roller can understand. Upon closer inspection, they combined neatly. Later, I solved a bug introduced when adding support for counting dice sets against the total [subtraction] by re-arranging lines of code from tokenizeInput in before lines from cleanInput.

Another thing that’s been bothering me has been the inability to pass off the context from Discord messages to other functions. As far back as the first stint on this bot, I wanted a function so I wouldn’t have to repeat myself in-code when sending a copy of returned messages to the command line. I achieved this with a function that logs, then returns strings I pass it.

print(“Hello World!”)
await ctx.send(“Hello World!”)

becomes

await ctx.send(broadcast(“Hello World!”))

Furthermore, I also played around with the ‘or’ operator. As I began work on implementing more features, of a trio of lines used for grabbing a user’s server nickname – or baring one, a user’s global account name. The way Python works, I was able to use

(ctx.message.author.nick or ctx.message.author.name)

straight in my broadcast liner function.

My general philosophy for this project has been to come up with a creative approach instead of just patching problems as they pop up. The readability of code will go down sharply as efforts to limit the amount of guesswork patching are neglected. As it stands right now with the next section’s features unfinished, I’d say this should be something I can come back to in a few months and make sense of faster than recoding it from scratch.

The Pythonic Way

Change of plan: implementing a save system can wait. While researching save file systems, I happened across an article on askpython.com about so-called “pythonic coding” [1]. Conventions change over time. I’ve noticed this first hand over this project as my style has noticeably drifted as I’ve consulted resources from over the past decade. Looks like I’ll be going through my code again and bringing my code up to standard where I can.

My first task was converting to using fstrings (f-strings – I cannot find anyone saying for sure what the f stands for, but I will be thinking of it as “format” until someone corrects me) when putting variables into strings. It went pretty smoothly, though I did have to be careful about using single vs double quotes when dealing with string data, such as keys in a dictionary. I’m not going for total conversion. If a print statement contains just one string and one variable, I can separate them with a comma. Anything more, and it’s time for fstrings.

The enumerate() function for getting loop indexes was one I’ve read about before, but didn’t appreciate until now. Similar to fstrings, enumerate has its place – specifically anywhere I used pointer variables. Now that I look at my code, my most annoying use of pointers was in that ugly function I’ve already replaced. Nevertheless, I was able to combine a pointer and its supporting code into for-loop statements. Similarly, I employed range() in a while-loop being used for a similar indexing job.

The AskPython article also brought up naming conventions, but I’m putting a hard pass on that one for this project. Variable names are fragile, and only work if they’re all converted at once. I’m not up to that level of potential breakage right now.

Takeaway

Code takes a lot of work to maintain and ensure readability. Add a seemingly innocent feature or consolidate chunks of code that belonged together all along, and you’re liable for something breaking and going unnoticed. I’ve lost track of all the bugs I’ve squashed, but I try to commit it to Git whenever I have a stable version, documenting my progress since my last commit. I still haven’t looked into using its other features yet, but it’s good practice making improved quality commits, and I know that’s worth something, even if it doesn’t feel like it yet.

Final Question

Have you ever taken up [part of] a set of standards for projects, even mid-project?

I look forward hearing your answers on in the comments below or on my Discord server.

Work Cited

[1] pankaj, “Pythonic Way of Writing Code,” [Online]. Available: https://www.askpython.com/python/examples/pythonic-way-of-coding. [Accessed Sept. 19, 2022].

I Upgraded My Python Discord Bot (Week3)

Good Morning from my Robotics Lab! This is Shadow_8472 and today, the development cycle continues on my Discord dice bot. Be sure to catch the last two weeks’ posts before reading this one. Let’s get started!

The Development Cycle

My bot will never truly be “done.” In the past, once software was released, it was typically tied to physical media. New features and bug fixes were either integrated into fresh copies or sold as expansion packs. In more recent times, your license to run a piece of proprietary software is tracked by an online service like Steam, and updates are usually free of charge. When you’re developing a piece of software internally, versions mean less and less until some deadline is reached or it’s “good enough” to stop work for a while.

When I stopped work last week, I was not “good enough.” It was pointed out to me that challenge dice for Star Trek Adventures, the game I’ll be playing, count their challenge pips as normal pips as well. My dice engine actually already supported multiple pip types on the same side naively, so I adjusted the challenge die definition instead of bodging a solution.

Various other improvements behind the scenes included matching the double space from the engine file over in the bot’s main file, making the challenge dice roll command use an icon picture, and implementing the RegEx OR operator for screening input. And then I went to adding or subtracting constants. I must have spent an hour or two trying to shove it through the engine side of things. I lost count at four places they needed special handling along the way from parsing to tallying. Just as I was about to finish, I had the brilliant, but tardy idea to code constants as single-sided, single-valued dice. Another good chunk of coding time summed up in one critical line of code. I was sure to leave a comment.

As part of my little constant episode, I made several changes to the code in lots of hard-to-remember places. Fortunately for me, most or all of my unrelated work was on the bot side of things. Because I split the file between bot and dice engine, I was able to pull up an old copy of the engine from the “production” branch and make the relatively few needed changes.

Takeaway

At this point, I need to plan on finishing the month with this bot. The engine’s code is messy, and I’d rather put it away clean so I don’t have to start over or feel the need to black box it next time I need dice.

Final Question

RegEx gave me a bit of trouble working on it. I used https://regex101.com/ to design my expression, but some of the statements they used threw exceptions in Python. Have you ever had a version mismatch give you a bit of grief?

I Upgraded My Python Discord Bot (Week2)

Good Morning from my Robotics Lab! This is Shadow_8472 and today, I am adding some features to my Discord dice bot. Be sure to catch last week’s post before reading this one. Let’s get started!

My Golden Standard

Dice come in many forms. Outside of specialty die collecting, the hobby of role play games will tend to land you with the widest variety to determine success/failure of fictional characters as their real-world players throw them into the hands of the equally fictional Random Number God (RNG*). Most people are familiar with cubic dice with faces valued one to six, but both the shape (read: number of sides) and the faces can be changed.

The role play system Gensys and its Star Wars themed ancestors go so far as to use as many as three types of pips on their narrative dice – it’s even possible to get both a “success” and “advantage” from the same face! There exists a dice roller by SkyJedi on GitHub for this system, but I would like a bot I can look at and understand. It serves as my gold standard, but I can settle for bronze.

*(officially RNG means Random Number Generator)

About My Bot

This bot constitutes my first major project where I have a discrete engine I will likely re-use later, so I’ve done my best to keep the engine clean at the expense of no time to redo the main bot’s .py file. The biggest question I had to ask myself over and over again was this: Engine or implementation? Is this feature specific to this function, or does it belong in the engine? I defined my engine’s role to begin after input has been screened, and lasting until it’s returned the structured results. I also included a separate tally function for summing the results.

Part way through testing, one of my friends tried subtracting one term from another. I had to re-write a good chunk of the otherwise mostly working engine, but I came out with a product that can pass common mathematical operators without choking.

I finished this week with a presentable bot. I didn’t get all the features I was after, but it will work for my upcoming Star Trek themed game. My main must-have feature is a set of “challenge dice” with the faces 1, 2, 0, 0, C, C. Multiple pip types was an important feature on this engine

Tricks Learned Along the Way

In no particular order, I taught myself a trick or two.

Space-indented comments:
Debugging is all about wall of text. At one point, I had a function with a triple for-loop to split input strings into tokens another function could evaluate one at a time. Six hours I bickered with it until success. That victory is in part thanks to my idea to indent debug messages based on how many nested loops each one was in. On my way to a break, inspiration hit and within five minutes, I had replaced the whole thing with only three lines of code.

When to Spaghetti Code:
Programming is an art form. Just as sketch work is drawn/painted over, so too are new features written quickly and sloppily, made to work, added to, tweaked, extended, and eventually depreciated as early code is replaced by a more elegant solution. Messy code is hard to develop, but a little more isn’t going to ruin your day if it’s that little bit more to round off a major version before a deadline. As you code, you will pick up better ways to do things, and you will want to go over your old code as you are able.

Clean Your Code Regularly:
I don’t know why I need to remind myself this, but I have a hard time deleting massive code blocks once a functional replacement has been coded. It feels good being able to revert quickly, but I’m working over SSH in a terminal window, and scrolling through uncollapsible blocks of code all the time isn’t fun. My advice is to comment out the old code being removed and make sure your program still works before removing it all together and verifying that it still works. The same goes for massive chunks of debug code built up over a long debugging session. If a whole file needs a major quality bump, I found it helpful to make a big block of comment I can move functions above as I perfect them.

Git Is a Tool to Practice:
I am using Git locally on this project. Right now, my skill level is git add and git commit. Meaningful comments will come with time as will checking out a previous version will come with time. I’m not working with an online repository, so I don’t need to worry about push or pull, though I am aware of them. In the meantime, I keep a special place for my most recent stable version.

The Development Cycle Loops:
The development cycle doesn’t end with release. Early versions will be flawed. Even now, I’m considering using those picture icons to the dice output. Always a new feature. Support code can take even longer to wrestle into existence if the clean implementation re-writes half the engine.

Takeaway

I would like to stress that I’m finding it difficult to write about writing code. Tangible progress is often non-linear. There are a lot more options than when you’re working with someone else’s program. In some ways, writing a program is more like writing a story than wrestling a Windows game into working on Linux. Once you’ve figured out the interface to set up your canvass, you are in full command of the ideas you express.

Final Question

What do you think of this “Lessons Learned” format?

I Upgraded My Python Discord Bot

Good Morning from my Robotics Lab! This is Shadow_8472 and today, I am tailoring my simple Discord dice bot for an upcoming role play. Let’s get started!

Project History

A while back, I built a very simple dice bot for Discord [1]. In its current form, it can be used for rolling an integer number of dice with another integer number of sides (3d6, for example) provided at runtime. A lot of effort also went into learning the Buildah/Podman OCI container implementation so I could clean up afterwords if something got messy. In the end, I managed a container with a mounted volume I could reprogram on the fly without entering the container.

Containerization with Podman/Cockpit has been treating me very kindly. This week, I was able to add environment variables on container creation, allowing me to work a bot token and branch label into the container itself. I also created separate development/production branches for short-term testing and long-term operation respectively, though in effect, I’ll also be keeping an old version around to act as a “stable” branch which will have its code integrated into the container itself instead of relying on an attached volume.

During this week’s work, I figured out how to test code segments directly in the interpreter. I also played around with separating core functions out of the main .py program file, and found they imported cleanly into the live interpreter. As a result, my debugging efficiency improved quite a bit.

Total Rewrite

There is a meme out there where a programmer can write a program such that only he and God know how it works – after a time, only God will know. A good programmer will return to such a problem with ideas for a more elegant solution, though it usually means rewriting from scratch. The core dice rolling functions for my first dice bot were hacked together and barely able to roll an arbitrary set of XdN dice where X in the number of dice and N is the number of sides.

This time, however, I’m programming for a game with non-standard dice faces. No way was I going to shove that into my old bot core. Instead, I spent around eight to ten hours crafting a new monstrosity I can reuse for other bots in the future and handles multiple types of die pips on a side at once. I focused on hacking it into my existing bot code (which I have yet to fully decipher), but it wasn’t so bad. What I do know is that I obsoleted several bits of code and about half as many comments intended for future work on that ugly line of development.

My new functions begin with the assumption that N can be defined as a list of sides, which in-turn list their respective kinds/counts of pips. For when N is int type (integer), I have some code to build a die face-by-face in that format. All the dice are loaded into a list, and that list is passed off to another function to actually roll the dice and tally up the results.

Worth noting is that I’m starting to make git commits to savestate my progress. I’m not worrying about proper form right now, but I’m counting on improving over time.

Settings Files

The ability to save/load data –while not strictly needed for a simple dice bot– is an important skill to develop. My brief research showed that while there’s any number of ways a Python programmer may implement settings, a somewhat standardized and end-user friendly method is using .ini files. While I may not use them this week in this project, I will want to remember this side goal. I’ve invented my own scheme before, and it wasn’t fun.

Takeaway

It’s really difficult to communicate all the little challenges that come up without looking/feeling like an idiot half the time because I missed something and another half because of the jargon. I’d have to say most of the time I spend coding is trying to understand what I just wrote. I still haven’t completed all the features I need, so the plan is to cover the rest of this revamp in next week’s post.

Final Question

What were you working on the first time you successfully extracted a part of your program into a separate document?

Works Cited

[1] Shadow_8472, “My Discord Bot Understands Strange Dice,” Feb. 28, 2022. letsbuildroboticswithshadow8472, [Online]. Available: https://letsbuildroboticswithshadow8472.com/index.php/2022/02/28/my-discord-bot-understands-strange-dice/ [Accessed Aug. 29, 2022].

Containerized Development Environment

Good Morning from my Robotics Lab! This is Shadow_8472, and today I am building my own dice bot for Discord using containers. Let’s get started!

Overview

In last week’s post, I learned how difficult a Discord dice roller is to find that is either already in a container or suitable to install into one. However, I did develop my skills along the way, and I believe I’m ready to make a bot of my own from scratch.

Development Environment

None of my machines had Python on them (except perhaps excluding the odd Raspberry Pi I have laying around), so the first order of business was to set up my own environment inside a container. There are a number of base images for running Python, but for development, I chose the latest official Python.

Podman pull python:latest

I went back and forth several times with the geometry of my bot project. I ended up stashing my work last week into a directory and making another to hold this round. When I was done, I had three files of consequence: Dockerfile, requirements.txt file, and a directory called home.

When I tell Buildah to make an image, it first goes to the Dockerfile:

FROM python:latest
COPY requirements.txt .
RUN python -m pip install -r requirements.txt

This takes the Python image, copies in requirements.txt, and references it to install any dependencies that don’t come with Python by default, namely as discord.py. As such, requirements.txt had a single line:

discord

Container base images leave out a lot of quality of life programs one might expect on a normal Linux install, like a text editor. That’s where the home directory comes in.

podman run -t -v /home/shadow8472/<bot project>/home:/root:Z -i dice_development:latest /bin/bash

This command has Podman use that image to make a container, mount that home directory into the container as /root (:Z is to tell SELinux it’s all good), and start a bash shell. From here, I can

podman attach <container name>
cd /root

and run the bot’s .py file. With the shared directory, I can edit it from the relative comfort of the host computer, ButtonMash, and go straight to running it inside the container. Best of all, the container will keep running with the bot if my connection to the host is broken, like if the network drops out or if I willingly close the terminal window. When I want to keep working, I can reattach the container.

Login token as environment variable: will my mental innovation of using the dockerfile to assemble it into a format Python understands work?

Development

I don’t have many comments about the actual development of the bot. It’s been a while since I actually programmed, and it’s been longer still since Python. I learned the basics of async programming a few years ago, and discord.py is building on that introduction, but I’m not setting out to master it just yet.

On my way to first stable “release,” I was coding up a loop meant to fill an array with die rolls. A bit of C++ grammar slipped in, and only after asking around did I identify it on my own and fix it. Another time, I coded an echo! function to practice passing command extensions into my code.

Most of my time went into constantly overhauling the internal logic. My early prototype was hardcoded to build its output string one die at a time, but I had to rework the back end to store the dice as they came in so I could sum them up and display that separately. All this was done while trying to keep it in a near-functional state.

But there are times a feature needs invasive re-working. I have some code in my comments that eliminates string manipulation from a function that should only be dealing with numbers. I’ve tried learning a popular version control system called Git in the past, but I can feel that I might actually stick with it this time. I’ve started reviewing commands, but I have yet to start using it as of writing.

Side Project

My week started off rough. I found my Derpy Chips workstation with a blank screen when I went to pull up last week’s post for some last minute proofreading. Initial diagnostics showed the screen blanks 0-30 seconds after making a picture from waking up. I got some help pouring over journalctrl and Xorg logs, but everything looked normal. The problem persisted through rebooting, pulling the GPU (graphics card), booting to an external drive, driving the monitor with my laptop, and even using a DVI cable instead of an HDMI. Final diagnostics turned up a dying backlight. I re-enlisted a personal museum piece –a vintage cathode ray tube model– to fill in until a suitable replacement can be procured.

3D Printing

Turns out that while I can play Minecraft just fine on integrated graphics, Slic3r pushes a few too many polygons for a smooth experience. I can’t run the old monitor off the graphics card because the card doesn’t have a VGA connector. Until I get a replacement monitor –or at the very least an adapter– I’ll be stuck with printing pre-made .stl objects or else installing Slic3r on another workstation.

What I did instead this week was make my own profile picture for the dice bot. I found a few dice I used to role play with and set them up in my photo booth. I even used a bead from printing the Z-braces to support a die in the back. I tweaked the picture a bit in GIMP to make it look more like the actual dice before uploading it for my friends’ enjoyment. It was not fun to fight the .jpg compression the entire way.

Takeaway

Programming is a completely different side of technology than systems management. It’s been a rewarding experience assembling my own environment to work in. It will need even more tweaking if/when I need to adapt it for use with an IDE, but those are for people who either already know everything that’s going or or those who don’t care to learn what’s going on at a basic level.

Final Question

Async functions are weird. I am after a small function to both log and send a response message. How do I pass an async call to another function? Am I even asking the right question?

I Buildah Discord Bot

Good Morning from my Robotics Lab! This is Shadow_8472, and today I am working on a dice bot for a role play on Discord for a few friends. Let’s get started!

Seeking a Container

Many projects are like a tree in terms of having a general sense of progression up from the root and the fine details take most of the work to perfect. This week, my project was a rose bush. I have an end-goal in mind, but I want to run it from a container, a technology I still haven’t fully introduced myself to.

My plan started out simple: find a Discord dice bot already in a container and get it on the server. DockerHub, a sizable repository of OCI (Open Container Initiative) container images. (Most places call them Docker Containers due to its popularity over the past ten+ years. Searches for help should be worded accordingly.) I located about the only one that met all my simple criteria and made myself a bot account for it.

Discord bot accounts are more similar to human accounts than I gave them credit for. The only big differences are that bot accounts log in with a token instead of a username/password and the interface is different to accommodate a program instead of a human. Just as you only interface with your account to make posts, so too does a bot interface with its account to make posts. Give the login to someone or something else, and Discord shouldn’t care.

Long story short, the bot was set up to use an environment variable when using the image to make a container. Once I got it logged in, it ran into other problems I wasn’t prepared to diagnose.

Learning to Build Containers

With my only obvious option a bust, I set my sights on a Discord dice roller I’ve used in the past made by SkyJedi for the Star Wars tabletop RPG’s and a generic spinoff called Genesys. The game I’ll be running is GURPS, which focuses on using 6-sided dice. Plus it has an interesting turn counter system for combat I’d like to try using. The rest is fluff to me this time around, so long as I get it working.

A quick tell of an intense week is when I spend a whole day watching tutorials on the same subject over and over again until I find one at my level. This was one of those weeks. I remember there being one breakthrough video right at my beginner level that let me go back and follow the logic of more advanced tutorials that previously dazed me. During this intense study session, I puzzled together how to use Buildah at a beginner level, what a Dockerfile does, and went from muck to murk in my understanding of Javascript.

Buildah is a for making OCI containers Podman can later run and another tool I haven’t yet explored can edit. Like Podman, Buildah’s corner of their ecosystem aims to replace Docker commands one for one. It’s a win for people switching from Docker, and a win for newcomers like me who can look back at pertinent Docker documentation.

Docker –at least– takes a layered approach to making containers. Using a Dockerfile (one word, also its file name with no extension) lets you build start with a base image and build it up layer by layer, one command at a time. Parts can then be swapped out and the image rebuilt as needed.

The Star Wars dice bot is written in Javascript. If there’s one thing I learned about Javascript this week, it’s that there are several “frameworks” for both front-end development (used to make things look nice for users) and back-end development (used for internal logic). More specifically: I needed to look into NodeJS, a back-end framework. Node has a few different base images on DockerHub, so I worked with the full one with the intent to swap it out for smaller ones for a much smaller image.

Assembling My Own Containers

It took a very special set of circumstances during my self-study for containers to really click. First, I’ve been working through the Podman plugin for Cockpit. Individual images and containers are presented in an intuitive way. Each container has tabs for info, logs, and console access. The life cycle of a container is tied to executing a single command, but if that command is to start a shell, I can interact directly with the container like I would at any other command line. I could test commands from there, and if I got something right, I could add it to the Dockerfile for my next iteration.

I was actually able to find SkyJedi’s Discord server and contact him directly. He wasn’t familiar with containers enough to help me, but he did direct me to a much simpler bot of his I was rapidly able to package into a Node container – and later a Node:Alpine container.

The difference was login token. No matter what I tried, the full dice bot would not accept its login token. I was not able to figure this out, though I came quite close.

Side Project

I’m considering a new weekly segment where I work with my 3D printer a little bit each week. This week, I printed up a calibration cube using everything I’ve learned so far. It’s still far from perfect. I still need to tune in my Z-braces, as I can tell from some faint ringing I can more easily feel by running my thumbnail across it. I still need to calibrate my E-steps, a process I may need to repeat a few times as I come up with interesting, new ways to improve my printer.

Takeaway

After all that, I looked ahead at a the database I’d need to deal with once I get the bot logging in, and I’m almost certain now that my time will be better served making my own bot in Python. Of minor interest, I was able to learn the curl command better. Rocky Linux apparently doesn’t come with wget, the terminal program I usually use to download files. Curl –on the other hand– copies the contents of links to the stream. When directed to a file, it does the same job as Wget. A cool trick I pulled off involved manually following a redirect link.

Final Question

I feel like a rose bush gardener with nothing to show for his work but the tools he’s learned to use along the way. What projects have you farmed up a bunch of dead ends on?