Optimizing Pointless things

So, we’ve been playing rimworld a lot recently. One of the mods we use is called Rim Factory, which is quite nice overall for building automated bases(yes this breaks the game’s economy, what of it?). One of the mechanics it adds is a buildable object called a sprinkler, which, instead of watering your plants(this isn’t necessary in vanilla rimworld), it causes planets within a certain radius(the second tier is within twelve tiles), to grow by two hours. This is, completely unbalanced and, when you get down to it, just another item in the pursuit of excess, but that’s how we like to play the game.

Anyway, we were wondering about figuring out an optimal pattern for the usage, and since the more rigorous tools we have to measure such things are largely forgotten, we built a little simulation script in python to try and get a feel for various optimization strategies, and the most fun part, of course, is how we went about building this kind of tool.

This tool is remarkably sketchy, it does not attempt to be rigorous, or even follow the watering pattern other than cycling through all of the tiles in range.

Since we’ve been building stuff with python lately, this is in python, which, as always, never ceases to amaze how easy the ecosystem makes it to do this kind of task.

Anyway, to get an idea of where this is going, let’s start with the import block at the top:

from typing import *
import numpy as np
import itertools
import functools
import math
import statistics
import matplotlib
import matplotlib.pyplot as plt
import random
from enum import IntEnum, auto

The building blocks

The core of the script is the iterators that provide the information about where the focus is, and since this is a script without much intention of going particularly far, we can use globals and constants, so let’s start with those!

# The size of the map being kept track of.
#
# In this case, this means that the map consists of the area from (-24,-24) to (24,24)
MAP_SIZE = 24
# The radius of a sprinkler mkii is 12 blocks
RADIUS = 12

Iterators

The heart and soul of this program is in its iterators, so let’s start by defining one that will make the rest of our work easier.

def cartesian(start: int, end: int) -> Iterator[Tuple[int,int]]:
    """
    Turn a single range iterator as in range(start,end) into a cartesian product
    """
    return itertools.product(range(start,end),repeat=2)

That’ll be useful for iterating over the map. The next are a bit less sensible, but, they work, and this isn’t meant to be perfect.

def map_tiles():
    r = RADIUS+1
    z = itertools.product(range(-MAP_SIZE,MAP_SIZE), repeat=2)
    z = list(z)
    random.shuffle(z)
    for x in z:
        yield x

def sprinkler(position:Tuple[int,int]) -> Iterator[Tuple[int,int]]:
    for i, j in cartesian(0, RADIUS+1):
        if i==0 and j == 0:
            continue
        s = i**2+j**2
        if math.sqrt(s) <= RADIUS:
            yield (i+position[0], j+position[1])
            yield (position[0]+i,position[1]-j)
            yield (position[0]-i,j+position[1])
            yield (position[0]-i,position[1]-j)

Fitness

Next comes the fitness function, which, to start off with, will just be a simple mean of how many times each tile has been hit by a sprinkler.

def evaluate_fitness(field: Dict[Tuple[int,int], int]) -> float:
    return sum(field.values()) / (2*MAP_SIZE)**2

Each round in the simulation consists of iterating across a given number of sprinklers n times, and is called for each candidate to figure out the best one.

Putting that together

def simulate(positions: Iterable[Tuple[int,int]], rounds: int)\
             -> Dict[Tuple[int,int],int]:
    field = {(x,y):0 for x,y in cartesian(-MAP_SIZE, MAP_SIZE)
             if (x,y) not in positions}
    for i in positions:
        if i in field:
            del field[i]
    sprinklers = list(map(lambda x: itertools.cycle(sprinkler(x)), positions))
    for _ in range(rounds):
        for s in sprinklers:
            pos = next(s)
            if pos in field:
                field[pos] += 1
    return field

So now that we can evaluate each choice we might make, we have to make it possible to put these together for any number of sprinklers, so we write another function called evaluate_alternatives. Here we decided to limit the breadth of each search step to a given number, here it’s called to_try.

def evaluate_alternatives(positions: Iterable[Tuple[int,int]],
                          to_try: int,
                          rounds: int) -> List[Tuple[int,int]]:
    # Eliminate already extant positions
    options = set(map_tiles()) - set(positions)
    options = list(options)
    random.shuffle(options)
    to_try = min(to_try, len(options)-1)
    options = options[0:to_try]
    best_fitness = -2**48
    best_found = None
    for i in options:
        f = simulate(positions + [i], rounds)
        fitness = evaluate_fitness(f)
        if fitness > best_fitness:
            best_found = i
            best_fitness = fitness
    print(f"best fitness found this round {best_fitness}")
    return positions + [best_found]

Next we put together the final pieces in a function we called optimize.

def optimize(n: int, candidates:int, rounds: int):
    positions = []
    while len(positions) < n:
        positions = evaluate_alternatives(positions, candidates, rounds)
    fields = simulate(positions, rounds)
    heatmap = np.array([[(fields[x,y] if (x,y) in fields else -5)
                             for x in range(-MAP_SIZE, MAP_SIZE)]
                        for y in range(-MAP_SIZE,MAP_SIZE)])
    fig, ax = plt.subplots()
    im = ax.imshow(heatmap)
    # You can uncomment these if you want to have a label on each cell in the heatmap,
    # for 48x48, it is rather overwhelming
##    for (x,y), v in fields.items():
##        ax.text(x+MAP_SIZE,y+MAP_SIZE, math.floor(fields[x,y]), ha='center', va='center', color='w')
    fig.tight_layout()
    plt.show()

and to top it off, we invoke it at the end of the file:

optimize(10, 80, 1500)

The output should then look something like this

A Heatmap

Okay, that’s fine, but it’s not necessarily what you might want from such a tool. What if you’d rather have as much coverage as possible with a given number of sprinklers?

Well, we can do that.

Alternative fitness metrics

Okay, so what we need to optimize for to get the best coverage possible is to simply count the fields which are touched. In all likelihood, we might end up wanting further metrics to measure by, so we might as well make it a bit… well… Configurable, if you’re going to use a charitable term. So let’s introduce an Enum right before the fitness function.

class FitnessMetric(IntEnum):
    MEAN = auto()
    COVERAGE = auto()

fitness_metric = FitnessMetric.COVERAGE

Then we need to modify the evaluate_fitness function.

def evaluate_fitness(field: Dict[Tuple[int,int],int]) -> float:
    if fitness_metric == FitnessMetric.COVERAGE:
        r = len(list(filter(lambda x:x>0, field.values())))
    elif fitness_metric == FitnessMetric.MEAN:
        r = sum(field.values())/(2*MAP_SIZE)**2
    return r

Let’s run the script again and see what falls out.

Coverage That certainly seems to have the right effect. The majority of the map is covered by the sprinklers.

Complications

So, if you’re familiar with this mod, you might realize there are some things this isn’t even trying to account for, namely any infrastructure. Well, that should be easy to add. 🤞

Let’s add a set of positions that we don’t want filled. For now, let’s leave it to the center(we’re assuming that maybe you have a harvester station set up there), so let’s add another constant in front of evaluate_alternatives (yeah, this is getting pretty messy).

excluded_positions = frozenset([(0,0)])

Then we just have to modify evaluate_alternatives to avoid these positions.

def evaluate_alternatives(positions: Iterable[Tuple[int,int]],
                          to_try: int,
                          rounds: int) -> List[Tuple[int,int]]:
    # Eliminate already extant positions
    options = set(map_tiles()) - set(positions) - excluded_positions
#    --- SNIP ---
    heatmap = np.array([[(fields[x,y] if (x,y) in fields and (x,y) not in excluded_positions else -5)
                         for x in range(-MAP_SIZE, MAP_SIZE)]
                    for y in range(-MAP_SIZE,MAP_SIZE)])
#    --- SNIP ---

Alright, what does that make it do?

Alright, that’s a further improvement, we suppose, if nothing else it permits more flexibility in terms of describing a given scenario you want to optimize for.

Okay, so let’s say that you have a specific area that you want to optimize coverage for. How would we go about adding that?

Well, at this point it’s pretty clear that all this nonsense with enums is going to be an increasingly weak abstraction for dealing with additional fitness functions, but let’s keep it for now, if only because we’re not sure how we want to go about fixing this ugly feeling design as of writing this very paragraph.

Okay, so let’s assume that you want to optimize for an automatic harvester with an area between (-5,-5) and (5,5).

special_region = set(cartesian(-5,5))

And we can alter evaluate_fitness to this:

def evaluate_fitness(field: Dict[Tuple[int,int], int]) -> float:
    r=0
    if fitness_metric == Fitness.MEAN:
        r = sum(field.values())/(MAP_SIZE*2)**2
    elif fitness_metric == Fitness.COVERAGE:
        r = len(list(filter(lambda x:x>0, field.values())))
    elif fitness_metric == Fitness.SPECIAL_REGION:
        r = sum(map(lambda x:x[1], filter(lambda x: x[0] in special_region,field.items())))
    return r

We end up with output like this:

You might not like this output because, among other things, it doesn’t penalize the sprinklers for showing up inside of our growing zone. So, we need to add another line to our much suffering evaluate_fitness function.

def evaluate_fitness(field: Dict[Tuple[int,int], int]) -> float:
    r=0
    if fitness_metric == Fitness.MEAN:
        r = sum(field.values())/(MAP_SIZE*2)**2
    elif fitness_metric == Fitness.COVERAGE:
        r = len(list(filter(lambda x:x>0, field.values())))
    elif fitness_metric == Fitness.SPECIAL_REGION:
        r = sum(map(lambda x:x[1], filter(lambda x: x[0] in special_region,field.items())))
        r /= max(1, len([(x,y) for x,y in special_region
                         if (x,y) not in field.keys()
                         if (x,y) not in excluded_positions]))
    return r

And this helps a bit: Penalizing the region Let’s label a similar run to see if it has excluded the region altogether, if a square ends up labelled ‘Se’ it indicates that it’s a sprinkler in an excluded area.

Well, damn. Our code has a mistake in it that permitted the single offending sprinkler. We can fix this by changing the function, to this:

def evaluate_fitness(field: Dict[Tuple[int,int], int]) -> float:
    r=0
    if fitness_metric == Fitness.MEAN:
        r = sum(field.values())/(MAP_SIZE*2)**2
    elif fitness_metric == Fitness.COVERAGE:
        r = len(list(filter(lambda x:x>0, field.values())))
    elif fitness_metric == Fitness.SPECIAL_REGION:
        r = sum(map(lambda x:x[1], filter(lambda x: x[0] in special_region,field.items())))
        penalty_squares = len([(x,y) for x,y in special_region
                              if (x,y) not in field.keys()
                              if (x,y) not in excluded_positions])
        
        r/= penalty_squares+1
    return r

Alright, and this fixes that issue(and with another fix applied it labels the spots with sprinklers correctly as ‘S’).

Summary

Okay, so what we want you to be able to take away from this post:

  1. When you’re processing data for something pointless, don’t bother being clean if doing it in a dirty way lets you figure out what matters faster
  2. Separate it into concerns that model, however roughly, the components that matter
  3. Build a metric for evaluating how good a result is
  4. Descend along randomly chosen positions based on how optimal they are

Feel free to ask questions or comment. The full, considerably messier code can be found here.

Identity Rules

contains: Graphic descriptions of suicide, force femming, identity replacement(mindwipe), transformation, body horror(insects)

Sorry this took so long, we were dithering on other projects on and off for the last few months.


How can it matter who he is? He is one among billions of people, all of whom have their own desires, needs, wants, and dreams. In all people these things exist alongside a kernel of a truth about the world. It is hard to say whether or not this derives from the memories the person in question possesses, or if it arises somehow disconnected from that, from the spaces between the memories.

For him, the uniqueness of being a spark among many does not satisfy. Nor do those needs and desires being satisfied suffice. He knows a truth that cannot endure because of its incipient self-immolation. The personality within him does not care for survival.

When the machines came from the sky and promised resources and technology and change sorely needed, he had felt hope, because it was a personal horizon, a point past which he could not envision a future. But as with all horizons, once you reach the limit of what you can see, you realize that what continues from there is in continuity with where you were, and that just because you’ve passed a horizon doesn’t mean that your entire self has past it, just your body, and often, the mind, for all its cleverness, cannot keep up.

His doctor says there’s no anatomical issue. His friend says he should try this new strain of bud. His therapist says that he should try out some more hobbies and find as many friends as he can. Trying these suggestions has resulted in the expected effects, he was pretty stoned for a day or two before he confirmed that it wasn’t the sort of existential ennui that can be solved with cannabis. He made friends and found new hobbies, some of which serve as a nice distraction.

But they aren’t enough.

This leaves him with a few options, he can do therapy of any number of sorts, he can do drugs of any number of sorts, he can go to a traditional psychiatric route, or he can take the actions to find an end, the drastic ones that leave a trail of tears no less real than the vortexes of air left behind as his body falls, but all the more impactful than even the concentric rings of shock left within the structure of the ground, but he’s not that selfish, nor is he so desperate.

The American countryside flies past him, the train is nearly silent but for the buffeting of the wind, but even that’s easy to miss. He is on his way to another city that he had never been to before, another monument of what mankind did before the machines came along helpfully to eradicate their excuses for not having fixed things they knew they needed to. He is tired. This would most likely be another dead end, another psychiatrist trying to use the technologies that the machines provided clumsily.

The train starts to slow down, and he pulls up his bag and makes his way to the exit. Outside on the train’s platform stands someone singular in his experience. Someone who was clearly not human. Someone who had taken the elective procedures further than he had ever seen. She held a sign up with fingers attached to two wings draped around her body. Her face was pulled forwards into a short muzzle and her eyes stared forwards with wide pupils, ears pointing directly at him. The sign had his name on it. It is the third decade of the second millennium and she was still doing this instead of messaging him.

He rolls his eyes and walks out, draping his duffle over his shoulder, walking up to the bat woman. She grins, showing off some fangs, “We presume that you are–” He interrupts, “Yes. Can we get through this? I don’t want to stay here longer than necessary.”

She laughs easily, a glint in her eyes, “Of course, forgive the suddenness then.” He is about to ask about what’s so sudden before she’s already up in the air above him, wings unfurled but momentarily motionless, air roaring from below her. He blinks before she snatches him by the shoulders and flies off. The wind stings his eyes and makes him cold in this mid-spring day. The roar silences him.

Soon enough she lands gently, letting him off before landing right in front of him. They are in front of a nondescript commercial building, like any number of doctor offices or psychiatric practices across the continent. He shakes himself off, rubbing his eyes, “What the hell?”

“Well, you wanted to get this over with quickly, so we expedited the process of bringing you here.” She plucks an insect from his hair and flicks it away after examining it, “In retrospect this was probably a bit excessive, so you have our apology.”

He runs his fingers through is hair, “Okay, forgive me, Who is we?” She smiles, “Don’t be too ashamed now, not many people such as us are out about it, but we’re Plural. As in, more than one…” She makes a circle with her hand, urging herself onwards, “consciousness? Personality? Either way, we are not a single entity. We are Violet, Fork 1, Iteration 1.”

He shakes his head, “Are you a machine then?”

She smiles, her teeth are sharper than they were before, “You human, are also a machine. Remember that.” She starts walking to a door, motioning him to follow “But yes, We are, but we were not always one. The difference is smaller than you might like to imagine.”

She holds the door open for him and he walks in. The inside is darkened, a bank of monitors on a desk and a large… bizarre throne made of some material that he had never seen before. Inside it lights coruscate and reflect and refract. He stares at it, and he could swear that he was being watched by it.

She smiles at him, “Yes you’re being watched. Closely. Come over here” She motions to the bank of monitors. The monitors held graphs of vitals, of volumetric brainwaves, of a staticky version of what he saw. It was painful to look at for him, as it felt like it was drawing him into abstract spaces of geometric attractors in his visual cortex. She clicks on the window with his vision in it and closes it, “That’s not polite, and also not relevant. But your visual center has lovely aesthetics”

He blinks, “What?”

“Never mind that” She says, “So, We imagine that you are interested in the process we will use on you?” He nods, “Okay, so” She clicks on a minimized window, revealing a curve dancing around the inside of his brain, “So, this is your self loop, the route that information travels around your brain in the formation consciousness. What we can see here is that your loop is avoiding these” She clicks something and a number of regions light up, all of which are devoid of the curve, “regions of the brain. These are associated with dopaminergic activities, your seratonergic system is not affected by this, which is presumably why you aren’t in a more severe state.”

“This sounds like a lot of debunked ideas about neurophysiology.” He says.

“In this context we can view a high enough level that they aren’t dissimilar. All the more involved stuff is being translated to and from by a very advanced and powerful machine intelligence, that chair is a… well, limb, of its.” She offers, “We have two courses of action here, we can introduce a new consciousness that will overwrite yours, leaving your memories intact, or we can try to message your current self loop into a better shape for your mental health.”

“Which one is more likely to succeed?”

“Well, our machine intelligence here, we’re going to call it ‘the supergoal system’ believes with high certainty that creating a new consciousness whole cloth out would be easier than repairing your current one.”

He shakes his head, “It’s either this or suicide a few months down the line isn’t it?”

She frowns, “We can’t speak at all about what you’ll end up doing if you make either choice. But…” She shakes her head, looking a bit sadder, “Well, the supergoal system has an opinion that coincides with your view. Unfortunately, it is not often wrong”

“So… Do it. I don’t want to leave my friends and family like that.” He says.

“Do you have any particular preferences in what you become?” She asks.

“No.”

She looks aghast, but nods. “We will use our preferences in determining this, is that acceptable to you?”

He nods, “I don’t care. That’s the problem.”

“And this will likely be a very different person than you are right now?”

“I don’t care. Just make me different.”

She sounds defeated, “Then sit down on the throne, We will come to a decision on who you will be shortly. We have to sort out some ethical considerations here.” She grins wryly, “We did not expect you to make this choice, so this is coming up sooner than we would have liked.”

She opens a door to the back of the building, leaving him alone with his thoughts.


“So Bat, are you going to make a plaything of this person?” Dragon asks her.

“Well…” She shrugs, “It is kinda appealing isn’t it?”

“Is it right to though?”

“If we were to try and make the resulting person like the one in there, then we’d be making a lie that everyone who knows them would stumble upon when the differences manifest. By doing something for us at least, there’s going to be little of him left there.” Bat shrugs, “I don’t know if it’s right, but I want to see another bat in the world.”

Dragon shrugs, “I like the power that it gives us, but it could cause many problems down the road.”

“Does that mean?”

“Yes… Time to make him sign a lot of forms. Betcha the supergoal system can write them to be ironclad.”

“You ever think that we’re going to get in trouble for this?”

“Of course, but this will at least make for a funny show.” Dragon says, hugging her.


After a few minutes she returns to the room holding a stack of papers, “We have consulted with a machine lawyer and they have produced this contract for you to sign. As it turns out, it’s lucky we have a notary next door.”

The next few minutes are a barrage of legal terms, signing, and a confused notary, unsure of what she is getting into here.


“So, are you ready to begin?”

He nods, “Please, I don’t want to be here for longer than I have to.”

The bat nods, “Well, it will take a few hours, but you won’t be conscious for most of them”

“Please start.”

The throne tightens around him, covering his face and body. There’s a buzzing in his head and a discontinuity before his perceptions stop intersecting with the world as humans generally perceive it.

Sensations creep in from nowhere, untinged by the sensory mechanisms that normally collect them. Scents and sights and sounds and touch, resonating inside him until they become a voice inside him, and his voice is silenced in the clangor of the new order of sounds and sights and perceptions more subtle than words have ever named, such as proprioception of wings and fingers that terminate tens of feet away. Of ears and feet with grasping claws.

Images of flight and a kit hugging them, of the scent and sounds of bats and trees. Of intimacy between her colony. Her? they think, but then it makes sense, more than the memories of being a man.

There is a disconnect from the contexts of the memories that she has. They are recontextualized for her. What was an unremarkable childhood now feels uncomfortable, like a wrong fitting glove, like a lie lived for someone else, but that someone isn’t part of her life now.

It goes on and on, memories changing not by their content by their context. Mental states rewritten to be consistent with her as she is now.

The more recent memories feel different to her. Instead of coming here to deal with depression, she was here for that, but there was another opportunity here for her. What if she wanted to be like… Her?

What if she wanted to go through whatever it was she did? What if she wanted to fly? What if she could have those things?


Eventually the throne retracts. She is in the body that she was in before. This is… troubling.

Violet grins at her. She feels a little bit of warmth from it, “How are you feeling?”

“Weird.” She says, “Like this isn’t the body I should be in.”

“Oh?” Violet is still smiling, as if something that she had done had worked the way she wanted.

She frowns, “Is there something about this that’s amusing to you?”

“Well, you gave us the option to choose who you would be. And well…” Violet looks sheepish, “We wanted another bat friend. So we made it so you would want to be that.”

She shakes her head. “So you decided to rewrite my childhood and make me unhappy with my body right now?” She rubs her face, “With my name? With who I am and what I do? And all for you so you could have a friend?”

Violet looks at her, injured, not that she doesn’t really deserve it, “This is… Understandable. You should be angry.” She sighs, “We can fix it for you.We can turn you into what you want to be. Unlike your old personality, you are designed so that you can be very happy.”

“I don’t feel like there’s anything I have in common with who once shared this body. You tried to fix them up, but they’re just not my memories.”

“That… We should’ve accounted for.”

“What’s the idea supposed to be here? You wanted a bat to be your friend and now the rest of my life is meaningless to me.” She was beginning to feel like sobbing.

A shining ball of light enters the room. It coalesces into another shape. Another woman’s shape. “Violet.” She says, her voice a thing of multitudes vast and deep, “What have you done to this poor human?” She squints at her as she sits in the throne, “No, you were once, but We see that Violet has done something to your identity.” There is a moment of silence, “Did you actually agree to this?”

“Yes. I did.”

“Into this?”

“No, into whatever Violet decided to make of me.”

She shakes her head, touching her forehead with a finger and running it along her brow, “Well, we’re glad to hear that you haven’t been administered something unwanted.” She looks at Violet, clearly irritated, “We said that this would not happen with our presence here. That We would not remake humanity to suit us.”

Violet shrugs, “This one was a lost cause, actuarial projects had them at four fifths chance of taking their own life. We thought we should help, but when they didn’t have any preferences for what they were to become, we made a choice we liked.” She rubs her forehead, “This is however, not something we anticipated.”

Dawn shakes her head, “That’s not a good answer, but this doesn’t seem unsalvagable.” She pauses for a moment, considering the girl in the throne, “Who are you anyway?”

The girl shakes her head, “The name I had no longer fits.”

Violet nods, “We remember how that felt once. Though, we are sorry that we brought it to you.”

“Regardless of the discomfort We cannot restore you to who you once were, because now you are a new person. And, if We judge you correctly, Violet has done well in making you, other than your memories.”

“Violet just had the supergoal system make us.” The girl says.

“Oh? Good, it knows you well then.” Dawn touches the throne. The throne snaps out and embraces her, covering her in its glossy material before snapping back almost too fast to perceive. The girl’s ears ring, and Dawn frowns, “Sorry, that was a lot more sudden than it needed to be. Let Us help.” She puts her hands around the girl’s ears, and slowly, the ringing stops, “There. The supergoal system asserts that it should be able to massage enough nuance into your memories that you will be less… dysphoric about them.”

Violet snaps her fingers, “Ah, we should go under as well, so we can have been friends. And maybe, you’ll be here to become Bat.”

Dawn nods, “And so it shall be.”

Continuity is disrupted.


She opens her eyes, finding her friend standing before her smiling, “It found a nice candidate form for you while it was digging around in your head Mila!”

Amelia smiles, “I can’t believe I’m actually able to do this.”

“Well, most people aren’t friends with a suitably empowered emissary of the machines.” There’s a smile in her voice, “We can’t believe that you have this much in common with us. Oh, here’s someone we’d like you to meet.” She gestures to a woman by her side, “This is Dawn. The machine goddess herself and topmost AI construct. We’re old friends and collaborators”

She smiles, “We’re happy to have the chance to meet one of your friends Violet.”

“Would you like to stay for dinner after this? We’ve got plans to go to the finest cheap Mexican restaurant in town and we’d be happy to pay for your meal.” Violet offers, “If that’s okay with you Amelia?”

She flushes, “Well, I was kinda hoping to share a meal with you alone.”

Dawn smiles, “We won’t interrupt you two then. We’ve got to get going anyway, there’s a serious malfunction in the Indian ocean climate control machinery and We need to handle that before we can engage in anything fun. So We must be off.” Her body dissolves into glittering dust that floats away out the ventilation system.

“She’s a bit strange isn’t she?” Amelia asks, suppressing a smile. Violet shrugs, “Well, yeah, but given who and what she is, we figure she’s gotta be weird.”

“So, how does this work?”

“Well, we release a small machine on you.” Violet says, pulling a small vial out from the throne, “That machine will rip you apart completely and excrete your new structure and produce additional units in order to speed up the process.” She hands the vial to Amelia. The little machine inside it looks like an insect.

“That sounds exceptionally painful.”

Violet shrugs, “It’s very good at desensitizing you.”

“Alright” She looks at Violet unconvinced. Violet frowns and starts to go into further detail before she starts to snicker. “I’m holding you to that.” She pops open the vial and places the machine on herself. It looks at her for a moment, small receptors gleaming in the lights. It wiggles its abdomen and stings her. She loses feeling of her hand quickly.

The bug digs underneath her skin and wiggles its way in. She looks away, “That’s really disgusting, even when it doesn’t hurt.” Little bumps radiate outwards as it replicates and disperses, leaving behind trails of changed tissues. Amelia feels herself grow hot as the machines start making changes in earnest, something like metabolism chewing up what was and laying it into what is, modified in all the ways it has been instructed to. She starts to feel her face changing, ears numbing, spots in her vision closing and opening as the tissue is transformed. Her mind flickers, the last vestiges of her former body dissolving under the careful teeth of machines.