There's not much I can offer, since there are so many things that I don't understand and can't ever hope to truly understand. I simply don't have the background or skin color to have the visceral experience that so many of my fellow Americans do.

The state of things has reached beyond fever pitch. It's at the point where not saying something would be a disservice, and, if nothing else, I know the burning, incandescent rage from justice unserved.

People are afraid. They're sick. They're angry at a government which has failed them for generations. Violence is the voice of the silenced, and I understand the impulse to lash out. In another life I might very well be one of the people doing it.

That doesn't make it right.

To the looters, look at what you've taken. In your amassing of things, did you remember to tally the better future you've stolen from your peers?

To the rioters, look at the things you've broken. In the rain of glass and the percussive thunder of metal on stone, did you remember to spare the livelihood of those around you?

To the peacemakers, look at the world around you. In the hail of pepper spray and rubber bullets, did you ever doubt for a moment that you were making a difference? No? Good. You're better people than I am and you deserve every ounce of respect that this country has in it. Be proud of what you've done. The world has already asked too much of you, but now we need to ask something more:

So many of you have already interceded in violence sparked by those wearing your colors. Please continue to intervene where you can.

Look for the helpers. Take care of yourselves. Stay safe.

I made an orbital camera controller for someone who wanted help on the Godot Discord channel. Here's the source. When applied to a Camera Node it gives this kind of behavior:

The player controller is fairly straightforward, so I've not included it as a separate gist. For a Kinematic Player, one can move relative to the camera direction like so:

extends KinematicBody

export var walk_speed:float = 5.0

func _process(delta):
	# Walking in the direction the camera is pointing.
	var camera = get_viewport().get_camera()
	var dy = int(Input.is_action_pressed("move_forward")) - int(Input.is_action_pressed("move_backward"))
	var dx = int(Input.is_action_pressed("move_left")) - int(Input.is_action_pressed("move_right"))
	var move = (camera.global_transform.basis.x * -dx) + (camera.global_transform.basis.z * -dy)
	move = Vector3(move.x, 0, move.z).normalized()  # Take out the 'looking down' component.
	self.move_and_slide(move*walk_speed)

Don't Crush Me is a game about pleading for your life with a robotic garbage compactor. It came up many years ago during a discussion in the AwfulJams IRC channel. The recent advent of Smooth Inverse Frequency proved an ample opportunity to revisit the idea with the benefits of modern machine learning. In this post we're going to cover the building SIF in Rust, compiling it to a library we can use in the Godot Game Engine, and then building a dialog tree in GDScript to control our gameplay.

First, a little on Smoothed Inverse Frequency:
In a few words, SIF involves taking a bunch of sentences, converting them to row-vectors, and taking out the principle component. The details are slightly more involved, but not MUCH more involved. Part of the conversion to vector rows involves tokenization (which I largely ignore in favor of splitting on whitespace for simplicity), and smoothing based on word frequency (which I also currently ignore).

Really, one of the "largest" challenges in this process was taking the Glove vectors and embedding them in the library so that GDScript didn't have to read anything from a multi-gigabyte file. The Glove 6B 50-D uncased vectors take up only about 150 megs in an optimal float format, and I'm quite certain they can be made more compact still. Additionally, since we know all of the tokens in advance, we can use a Perfect Hash Function to optimally index into the words at runtime.

With our 'tokenize' and 'vectorize' functions defined we are free to put these methods into a small Rust GDNative library and built it out. After an absurdly long wait for the build to compile (~20 minutes on my Ryzen 3950X) we have a library! It's then a matter of adding a few supporting config files and we have a similarity method we can use:

Now the less fun part: Writing Dialog. In the older jam Hindsight is 60 Seconds, I capped things off with a dialog tree as part of a last ditch effort to avoid doing work on things that mattered. The structure of that tree was something like this...

const COMMENT = "_COMMENT"
const ACTION = "_ACTION"
const PROMPT = "_PROMPT"
const BACKGROUND = "_BACKGROUND"
var dialog = {
     "_TEMPLATE": {
         COMMENT: "We begin at _START. Ignore this.",
         PROMPT: "The dialog that starts this question.",
         ACTION: "method_name_to_invoke",
         "dialog_choice": "resulting path name or a dictionary.  If a dictionary, parse as though it were a path on its own.",
         "alternative_choice": {
             PROMPT: "This is one of the ways to do it.",
             "What benefit does this have?": "question",
             "Oh neat.": {
                 PROMPT: "We can go deeper.",
                 "…": "_END"
             }
         }
     },

I like this format. It's easy to read and reason about, but it's limited in that only one dialog choice corresponds to one action. For DCM I wanted to be able to have multiple phrasings of the same thing without repeating the entire block. Towards that end, I used a structure like this:

var dialog_tree = {
    "START": [ # AI Start state:
        # Possible transitions:
        {
            TRIGGER_PHRASES:["Hello?", "Hey!", "Is anyone there?", "Help!", "Can anyone hear me?"],
            TRIGGER_WEIGHTS: 0, # Can be an array, too.
            NEXT_STATE: "HOW_CAN_I_HELP_YOU",  # AI State.
            RESPONSE: "Greetings unidentified waste item.  How can I assist you?",
            PLACEHOLDER: "Can you help me?",
            ON_ENTER: "show_robot"  # When we run this transition.
        },

        {
            TRIGGER_PHRASES: ["Stop!", "Stop compressing!", "Don't crush me, please!", "Don't crush me!", "Wait!", "Hold on."],
            NEXT_STATE: "STOP_COMPRESS_1",
            RESPONSE: "Greetings unidentified waste item.  You have asked to halt the compression process.  Please give your justification.",
            PLACEHOLDER: "I am alive.",
            ON_ENTER: "show_robot"
        },

        {
            TRIGGER_PHRASES: ["Where am I?", "What is this place?"],
            NEXT_STATE: "WHERE_AM_I",
            RESPONSE: "Greetings unidentified waste item.  You are in the trash compactor.",
            ON_ENTER: "show_robot"
        }
    ],

This has proven to be incredibly unruly and, if you are diligent, you may have realized it's just as possible to do the same "multiple trigger phrases" in the first approach via some simple splitting on a special character like "|".

So how well does it work? The short answer is, "well enough." It has highlighted a much more significant issue: the immensity of the input space. Initially, it was thought that using a placeholder in the input text would help to anchor and bias the end-user's choices and hide the seams of the system. In practice, this was still a wrought endeavor.

All things considered, I'm still proud of how things turned out. It's a system that's far from perfect, but it's interesting and it was plenty satisfying to build. I hope that people enjoy the game after the last bits are buffed out (hopefully before GDC 2020).

Shuffling datasets is a fairly standard fare operation in data science. Without it, we risk training a model on a moving target which gradually shifts over time. For most interesting datasets, we can't fit all of the data in memory, but we'd still like to be able to access it at random. Sure, you can generate a random number and seek on disk to that location, but that doesn't guarantee that the sought position will be one with a valid line. Jumping into the middle of a JSON blob and seeking to the end is bound to yield a bad time. Here is a brief solution:

class LineSeekableFile:
    def __init__(self, seekable):
        self.fin = seekable
        self.line_count = 0
        self.line_map = list() # Map from line index -> file position.
        self.line_map.append(0)
        while seekable.readline():
            self.line_map.append(seekable.tell())
            self.line_count += 1
    
    def __getitem__(self, index):
        # NOTE: This assumes that you're not reading the file sequentially.  For that, just use 'for line in file'.
        self.fin.seek(self.line_map[index])
        return self.fin.readline()

    def __len__():
        return self.line_count

It is available here as a Python Gist:

Overview

Goose Game, but instead of a horrible goose you are a good dog whose job it is to break up fights at holiday parties. Let's game it out.

Gameplay

Simulated people will run through the motions for a party. Each guest will have a set of desires and some personal goals to achieve. For the sake of gameplay, everyone that can see the dog (player) will attempt to intervene when the player is misbehaving, rather than deferring to a host or someone else.

Visuals

Something visually simple with bright colors and simple geometry. This game should be very easy on system resources and should operate smoothly on low-resource machines or mobile devices. One option:

Low Poly pack by @Quaternius

Audio

High-fidelity, cheerful music with cute sound effects. Human speech and communication should _not_ be real human speech, but should be some sort of non-lexical vocalization like Simlish.

Interesting Challenges

Fundamentally, the interpersonal interactions will drive the game. For replayability (and developer sanity) it is desirable to have conflicts emerge naturally from NPC interactions. Additional household events can add color or fun as discoverables, but the most fundamental piece of the game is the temperature of discourse.