Tuesday, September 23, 2014

Part 5: Dynamic Plots

This is part 5 of a series of posts on procedural generation of content in Idamu Caverns.  You may want to go back and read the earlier posts as well.

The idea of procedurally generated plots seems like the holy grail of computer programming.  Not only could a chunk of code doing this revolutionize the game industry, but it could put a bunch of script writers for TV and movies out of business as well (especially for a lot of mediocre sitcoms on television).

Luckily for those writers, accomplishing this seems to be very difficult.  If you know of anyone doing it, please drop me a line and tell me about it.

While that doesn't mean it's impossible, I expect that it's too difficult for a lonely developer to build on his own.  Of course, that makes me all the more determined to do it!

I'm writing this blog post prior to 99% of the code being written, so most of what I'm about to say is speculative planning.

The key to most programming problems is to break the problem down into pieces and solve each piece independently of the whole problem, while keeping the solution flexible enough to be usable even when unexpected things are discovered about the overall problem.  Abstraction is key (think about the Map interface in java, and how you can easily swap out different implementations based on the specifics of the problem).

That being said, the part that is currently written is to avoid the complexity of each creature in the game having to track their attitude toward you independently.  Instead, each creature belongs to a faction, and reputation and other things are tracked by the faction.  Each creature, then, stores only information about what faction they belong to, and some indication of how closely they follow that faction.  This quickly allows the few factions to remember extensive and detailed information about the player while each creature (of which there are many) only has to remember two small pieces of data.

Organizing things into factions is only the tip of the iceberg.  The complex and interesting part is the code that I haven't even started yet: how do the intelligent creatures react to the player based on faction standing (a large part of this is that I haven't created code for intelligent creatures yet ...)

Like mentioned in earlier parts of this series, completely random information is usually not the best way to do things, and this approach should avoid that.  If the player meets the Spider Queen and she randomly decides whether to be friendly or hostile, that's not very engaging or realistic.  However, if the Spider Queen is hostile because he's been killing and looting every spider he's met since the beginning of the game, that becomes interesting.  Not to mention the fact that it encourages the player to come back and play additional times, perhaps not killing so many spiders the next time through to see what benefits there are to befriending them!

Interactions become more interesting based on this mechanic alone.  If the Cat people and the Dog people are at war, and the player has an excellent reputation with the Cat people, she's not going to like the first encounter she has with the ruler of the Dogs, even if she has avoided fighting them.

But are the Cats and Dogs at war?  This is a place where random generation is interesting.  If each run through the player doesn't know whether there is an uneasy peace or all out war between these two factions, then the replay value of the game goes up quite a bit!

This alone could be considered a procedurally generated plot.  If each faction has a randomly generated attitude toward every other faction, and there are rules and code in place to control how factions behave toward each other depending on whether they're allies, or at war; then we have a state of affairs that the player has to contend with that changes with each play through.

Many other behaviors can be the result of faction reputation: Do faction members attack on sight?  What kind of prices do merchants offer?  What quests are available?

Writing this got me to thinking.  Each of the monsters in the game has a hardcoded reaction to the player at this time.  Spiders always hate you.  Rats ignore you.  But what if this were based on faction, and the faction attitude was random?  One time you pay the rats hate you, the next time they ignore you.  Depending on the other attributes of the factions, this could change up the game quite a bit.

The key to making this work programmatically, is good programming interfaces between the code that handles behaviors and the code that manages creature-specific attributes.  Doing that well is a completely different discussion.

This is the last post in this series for now, but I may come back to the topic another time.  If you've enjoyed these posts, please share, and consider downloading Idamu Caverns and becoming a Patron.

Thursday, September 18, 2014

Part 4: Quests

I'm writing this blog post as I create the initial code.  This is Part 4 of a series of posts on procedural generation in games.

I've been thinking about quests in Idamu Caverns for quite some time.  Now that it's time to start implementing the code for the next feature release, I've been thinking about them a lot more.  Quests form an integral part of many game genres, and previously I'd thought it would be poor form to have them generated programatically.

However, the last few days, as I plan out my actual strategy for the code, I'm realizing that quests aren't that different from a lot of other game aspects that can be generated.  For one thing, most quests fall into one of a few categories with aspects that are easily randomized.  At it's core, a quest contains the following elements:
  1. Someone/something to communicate with about the quest
  2. A thing to do for the quest
  3. A reward for the quest
Number 3 is the easiest to generate: pick a valuable item that the player doesn't have and offer it as a reward, possibly in addition to points added to the score, or an achievement if the game supports that mechanic.

Number 2 is only slightly more complex.  Wikipedia has a list that breaks quests down into categories, which basically handles everything I need to say about this.

Number one is where things get interesting.  On the surface it seems simple, spawn an NPC and put it somewhere the player will find, then use that NPC to manage the issuing and rewarding of the quest.  If it were that easy, though, you could just have a book of quests that the player can read from any time she wants.  One of the important reasons for creating an NPC to begin with is to create some sense of realism to the game.  Sure, I could simply place a Creature that says "Bring me $number $items and I will reward you with $treasure." and having a simple generator like that would be better than no quest at all, but it lacks the emotional power that many gamers crave.  Many gamers want the game to give them an emotional reward in combination with an in-game reward.  Something like, "thanks to you, my wife's spirit can finally rest in peace."  I don't know if I can generate that sort of thing with code.

From a programming perspective, all quest code has a fixed way in which it needs to interact with the rest of the game code:
  1. There needs to be a way to communicate the desired difficulty to the quest generator
  2. The quest generator needs to ensure that all the game objects required for the quest are populated
  3. The NPC has to communicate the quest to the player
  4. The NPC has to recognize when the quest is complete and reward the player
  5. If it's possible to fail the quest, the NPC has to recognize and react to that as well
There are other things that could be added.  For example, the player's reputation with the NPC might determine how good the reward is, or whether the quest is available at all.  There are also non-critical nuances, such as what happens when the player has received the quest but has not yet completed it, then talks to the NPC again.  In the end, however, the above five are pretty much the minimum for getting a quest engine running.

For the first round of quests in Idamu Caverns, I've decided to create a quest manager that can select from multiple types of quest generators when the time comes to make a quest.  That will allow me to gradually implement different quest types and have the quest manager be a central place to enable them when they're ready.

The early quests will be fairly generic in nature, and thus not have much emotional value to the player.  However, once the code is in place, it will allow me to easily write specific quests and interleave them with the generated ones, the overall goal being to quickly provide a wide array of shallow quests to make the game feel broad, then add quests with depth over time, to strengthen the game.

If you join the Idamu Caverns community on Google+, you'll see updates as they happen and will know when this new code is released.  If you're enjoying reading about the development of Idamu Caverns, or enjoying the game itself, please consider becoming a Patron to ensure I can continue developing it.

The next post in this series discusses dynamic plots.

Wednesday, September 17, 2014

Part 3: The Director

This is the third post in a series about procedural generation in Idamu Caverns.

Anything generated on the fly presents a challenge in maintaining game balance.  If levels are designed by humans, those designers will ensure that the levels are appropriately challenging, and pre-release testing will ensure that the level designers have done their job in creating levels that are challenging but not impossible.  When generating levels dynamically at run time, this testing isn't available.  Furthermore, since there's no linear flow to Idamu Caverns, a player could easily take any one of multiple routes, thus arriving at a point with different equipment and buffs accumulated.

It seemed logical to me that there needed to be an explicit section of code for maintaining this balance -- a game Director, similar to what was designed for Left 4 Dead.  The purpose of the Director code is twofold:
  1. Keep the game as challenging as the player requested.
  2. Keep the game fun and interesting.
When you start the game, you are asked to select a difficulty level.  This selection is stored as part of the game state, and the Director attempts to maintain that difficulty level throughout the game, adjusting for how well (or poorly) you play.

The code is called primarily at level generation time.  Once the map generation code has finished, the map contains rooms, hallways, stairs, and spawn points.

For each spawn point, the director is consulted at multiple times during the population process.

First, the Director is consulted to see if the spawn point should spawn a treasure.  Here the director is fufilling the role of keeping the game at the requested challenge level.  To determine this the Director keeps track of how many treasures have already been populated on the current map, and compares that number to a number derived from the requested difficulty, factoring in a small random element to keep things from being too predictable.  A higher difficulty setting results in treasure spawning less often, while a lower difficulty setting means more often.

When it's time to populate a treasure, the director is consulted again to determine what type of treasure to populate.  Here the primary goal is to keep the game fun.  The code has already determined to present you with something helpful at this point, so the goal is to ensure that it's populating something useful.  The Director essentially rifles through your inventory to see what you're currently carrying, and tries to present you with something that you need.  This is why you won't see backpacks spawning once you already have a backpack (because there's no value to having multiple backpacks).  The code here is still imperfect, so you will occasionally find items that you have no need of, but it does fair job for the most part.

Next the Director determines whether to spawn a monster.  The chance of a monster spawning is always 100% if no treasure was spawned, but a factor of the difficulty level otherwise.  While it seems odd to have unguarded treasure lying around, it's a matter of keeping the game interesting, as there aren't enough items yet in the game to have every monster drop something.  This is code that will have to be adjusted as the catalog of game items increases.

Selecting which monster to spawn brings up another factor in the Director's operation.  Each creature in the game (of which the player is one) has a challenge rating that can be computed at any time.  It takes into account every factor that makes the creature dangerous, such as damage resistance, maximum health, and potential damage dealt; and returns it as a single number.

The player's challenge rating is calculated, and the requested difficulty is added.  This results in the desired challenge rating of the monster to be populated.  To mix things up a bit, a random element (based on the requested difficulty) may increase the target challenge rating, resulting in a monster that's a bit more challenging than would be expected on that level.  The list of possible monsters is searched to find the one closest to the target challenge rating, and that monster is placed at the spawn point.

The Director does a good job of keeping the game on track as far as being fun and challenging, but it's too random.  Monsters and items are spread completely at random across each level, which removes any sense of coherency from levels.  I have plans to improve on this in the future.  Another challenge is that every new element added to the game upsets the balance of the Director's equations and requires some tuning.  This is a problem only because Idamu Caverns is being developed incrementally, and ensuring that each version is playable requires careful testing to ensure the Director is still doing it's job properly.

You should download Idamu Caverns and try playing at different difficulty levels to see how all this translates into a game.  Please also consider becoming a Patron to help ensure I can keep making games and giving them away for free.

The next post in this series discusses generated quests.

Tuesday, September 16, 2014

Part 2: Procedural Map Generation

This is part of a series on procedural generation in games.

Generating maps pragmatically is trickier than it seems.  First off, the ability to write a good map generation algorithm is highly dependent on the desired output.  As an example, I'm not aware of any first person shooter that uses generated maps, and I would be amazed to see one.  The nuances required for a good map in that genre are quite complex.  On the other hand, generated maps are not uncommon in the dungeon crawling genre, as it seems it's easier to make a useable map with code there.  Since Idamu Caverns is a dungeon crawler with generated maps, that will be the focus of this post.

I did a lot of research before starting on Idamu Caverns.  Probably the single most influential thing I read is this nine part series on developing Roguelike games.  It's quite a lot to read, but if you're planning on developing anything vaguely Roguelike, I recommend that you set aside time for it.

It's difficult to write this without the article becoming overly long, as there's a lot to be said on the topic, but one of the most important things to clarify before you start writing map generating code is to understand why you're doing so.  Not so much, why am I having code create maps instead of just creating the maps myself; but why do I need maps in the first place?

Unless you're writing a text adventure (remember those?) you'll need a way to represent game play graphically, but even with text adventures there were maps.  To get to the real answer, we have to dig deeper, to the core concept that exploring maps is fun.  Take away the monsters and loot and everything else, and there's a joy alone in exploring a map.  And map exploration is something video games do better than any other medium I can think of.

So, if you're going to have a map at all, you need to make sure that exploring that map is an adventure in itself.  This leads to another concept that I'll touch on over and over again: random generation is usually bad and should be used sparingly.

Taken to an extreme, a purely random map isn't even playable.  It would have a completely random tile on each square, and probably wouldn't even be traversal.  While such an extreme case may seem pointless to bring up, the rule of thumb that less random is better probably holds true in most cases.

The map generator currently used by Idamu Caverns is actually many layers of map generation, as well as many iterations of the code.

From the start, I recognized that many techniques of map generation existed, that I probably wouldn't get it right on the first try, and that the the different methods could produce maps with different feels and it may be useful to have more than one method.  As a result, the first thing Idamu Caverns' map generator does is to select a specific map generator.  This approach allows me to work on new map generators over periods of time, and leave them disable in the production code until they're finally ready, which works well with the frequent release model of the game.

The first map generator code was non-random procedural generation.  A long hallway was generated with the up and down stairs at opposite ends, and equally sized rooms placed equal distance along the hall.  The only random element to the map would be what monsters and items spawned in each room.  The advantage is that the code was simple, reliable, and fast; but the disadvantage was that the resultant maps were boring.  They had no entertainment value in themselves.  As a result, this original code didn't even survive to the first release version.

What replaced it was a random generator that worked by drawing random sized rooms, then connecting them with hallways.  This was considerably more interesting, but also more challenging to make work.  The complexity of connecting the rooms without having hallways that overlapped and created an ugly mess was challenging.  Early versions would frequently create maps with sections inaccessible from the stair.

Furthermore, even this was too boring.  Hallways were never interesting, it was difficult to allow them to intersect while preventing them from intersecting too much and in ugly ways.  With each room simple a rectangle, there was never anything interesting about the rooms.

In essence, there was too much random, and not enough human controlled interest.  I didn't need to be a genius to solve the problem, though, I just took some hints from the article mentioned above and programmed a method for extracting data on human-generated rooms and placing them instead of random sized rectangles.  This allowed me to draw any sort of room (or possibly a configuration of multiple rooms) and have the code randomly place it on the map then connect everything with halls.  The unintended benefit is that it allows the hall code to become simpler.  No longer were hall intersections allowed, instead, a small number of "rooms" are drawn that are essentially hall intersections.  The random placement algorithm puts these on the map occasionally and connects them to everything else as if they were a room.

The algorithm continues to evolve, and as it does it keeps appearing that less randomness is better than more.  One such optimization was that instead of placing rooms completely at random, the algorithm now breaks the room sizes into large and small, and places a few large rooms first, then attempts to fill in the rest of the map with small rooms.  This ensures that maps will always have a large room, and not simply be crammed full of tiny rooms.  It also avoids the uncommon, but annoying map with only a few large rooms.

A lot of the design emerged as I experimented.  Originally, I intended for every level to have a single stair down to the next level.  However, I couldn't quite get the code to guarantee that there would always be a path between the two stairs.  To shorten a long story, instead of ensuring that you could always get to the down stair, I embraced the concept of the occasional dead end level by creating workarounds that prevent it from stopping further gameplay.

There's a lot more I can say on this topic, so it's likely I'll do additional posts.

The next post in this series concerns keeping the game fun and challenging.

Monday, September 15, 2014

Part 1: Procedural Generation in Games

I was about to reply to a question on reddit when I realized the topic was large enough and of enough interest that it warranted a blog entry.

For those not familiar with the term, procedural generation of a game map would be a map that is generated by code in the game, as opposed to a map that is created by a human and simply shipped along with the game.  This is of particular interest to me, since Idamu Caverns uses a lot of procedural generation.

In case you don't want to visit reddit to see the original question, it basically boils down to asking what people think about procedurally generated data in games, such as maps.  The original question posited that procedural generation results in games with a lot of breadth, but not much depth.

An example given was Minecraft.  At first look, Minecraft seems to have both depth and breadth.  However, playing it for any length of time makes you realize that the depth is simply an apparency created by the tremendous breadth of Minecraft's procedural generation.  Minecraft has no quests, no real characters.  You can count the different types on enemies on one hand.  The terrain in the game starts to look repetitive very quickly.

I think the first lesson to learn from this is that significant breadth can give the illusion of depth.  So if you're going to make a procedural game, giving it a lot of breadth can make up for some lack of depth.

Let's take a much more classic game: Angband.  Everything in Angband is generated procedurally, but the depth of monsters and items that the procedures have to pull from is incredible.  Perhaps that's why Angband is still developed and played almost 25 years after it was first released, and has more clones and spinoffs than I can count.

So, another lesson is that depth + breath = immortal classic.

Looking at Idamu Caverns, I wanted it to eventually have the same "immortal classic" magic that Angband did.  I did a lot of research, and what I finally decided on was a hybrid approach, closer to procedural than fixed, but a combination of both.

The idea is that I would grab whichever method seemed like it would be best for a particular aspect of the game.  For example, I decided on procedurally generated maps, but I've decided on prepackaged quests.

This post could easily get very long, so I'm going to break it into sections and post updates every few days.  Some of the topics I plan to cover:
This list will probably expand over time.  Once I actually start writing new posts, I will probably come up with additional topics, realize that some topics need to be broken up, and who knows what else.

Thursday, September 11, 2014

Android and OpenGL

If you're doing any drawing in Android, you're using OpenGL.  It might not be immediately obvious, but as far as I can tell, even when you're not using the OpenGL-specific API, the work of drawing goes through the OpenGL code.

It makes sense, really.  If you're going to include OpenGL as a core API of your system, might as well make all the drawing functions funnel through it.  It probably makes the underlying code simpler than having multiple channels to the hardware, and if implemented carefully should be reasonably fast.

The problem I'm about to describe isn't something that's a direct result of using OpenGL.  It's more of a mistake that could happen any time one leverages libraries to create other libraries.

I found this while optimizing some drawing code for Idamu Caverns.  I'd decided not to use any game development frameworks for the game, so the drawing code talks to the Android APIs directly.

To summarize the situation:  The code uses a dedicated rendering thread to render the map to a Bitmap, then displays the Bitmap on the screen in the main UI thread.  This ensures that the UI stays snappy, possibly at the loss of a few frames here and there if the rendering thread gets overly busy.  It also makes some operations simpler.  For example, the render thread always draws the entire map, even if the zoom factor will result in only a small portion of it being displayed, so the render code isn't cluttered with checks on the screen extents.  Additionally, the UI thread can zoom and reposition the map without waiting for the render thread to finish updating it.

Unfortunately, these advantages were also causing problems.  At high zoom levels, the rendered bitmap was huge, despite the fact that most of it would never be seen.  But the memory use wasn't the biggest problem; I worked around that by catching OutOfMemoryError during Bitmap allocation and reducing the zoom factor until allocation succeeded.

No.  The problem was that on some devices, at some zoom levels, the screen just disappeared.  No exceptions, no explanation, no crashes, just no display.

Logcat finally revealed the source of the problem.  It seems that Android's drawBitmap() method works by creating a OpenGL texture from the Bitmap, then issuing OpenGL commands to render the Bitmap to the screen (note that I haven't looked at the code, but you'll see why I'm making this claim shortly).

The Logcat message that I was seeing:
W/OpenGLRenderer﹕ Bitmap too large to be uploaded into a texture (4096x4096, max=2048x2048)

A little research explains that OpenGL keeps texture memory separate from other memory, and that there is a hard limit.  This is no surprise.  What is surprising (and frustrating) is that exceeding said limit simply results in a warning message and failure of the code to do anything.  In my mind, this really should generate an OutOfMemoryError so the situation is detectable by the code.  Furthermore, how much texture memory is available is different for every device and it's somewhat tricky to determine how much is available (it involves talking directly to the OpenGL API, which seems cumbersome, especially in code that doesn't use OpenGL directly.)

In the end, I solved the problem by breaking the map into smaller sections and rendering each one independently (think of how Google maps works when you zoom).  This results in improvements all around: sections that aren't on the screen are deallocated and not rendered, and the UI thread doesn't have to wait until the entire map is rendered to start displaying, to name a few.

But best of all, since each map section is small, even at high zoom levels, the size never exceeds the OpenGL texture limit (at least not on any hardware I can find).  So my mysterious blank screens are gone.

If you want to experiment with this a bit for yourself, the following code should give you a place to start playing:

package com.gamesbybill.drawexperiment;
// includes omitted for clarity
public class Display extends View implements Runnable {

  public Display(Context context, AttributeSet aSet) {
    super(context, aSet);
    Thread t = new Thread(this);
    t.start();
  }

  private static int size = 1;

  @Override
  protected void onDraw(Canvas c) {
    size *= 2;
    Bitmap b = Bitmap.createBitmap(
      size,
      size,
      Bitmap.Config.RGB_565
    );
    Canvas bmCanvas = new Canvas(b);
    bmCanvas.drawColor(Color.BLUE);
    c.drawColor(Color.LTGRAY);
    c.drawBitmap(b, 0, 0, null);
  }

  @Override
  public void run() {
    while (true) {
      postInvalidate();
      SystemClock.sleep(50);
    }
  }

}

Add the following to the XML file for your main activity to make it go:

<com.gamesbybill.drawexperiment.Display
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

At some point the code will crash with an out of memory error, but look prior to that in logcat to see the OpenGL message that doesn't crash the app, but simply causes drawing to fail. The limits are different for every device, and once you know the limits for the device you're testing on, you can tweak the code to make the effect easier to see.

Wednesday, September 10, 2014

Formatting charts on Android with AChartEngine

AChartEngine is a great tool to allow you to quickly add charts of various types to your Android app.
As good as it is, it's unfortunately not perfect.  I'm currently running into some formatting problems when using larger font sizes.  I'll be opening a bug ticket shortly, but this blog entry will allow me to go into detail more easily than I could in the bug reporting system.

At the end of this post is source code.  Using that code, you get the following result when run on a phone:



You can see that the legend is crowding the bottom of the graph to the point of overlapping the X axis title.  This problem becomes more pronounced as font size increases, and is much worse on a tablet, where the code increases the font size even further.

I searched around in the AChartEngine source a bit, in the hopes of generating a patch that just fixed the problem.  I feel that the problematic code is line 104 of AbstractChart.java:

float currentY = y + height - legendSize + size;

Searching around the code, I couldn't come to an understanding of what that simple equation is supposed to be doing.  Instead, I took a brute-force approach and replaced it with:

float currentY = height - size;

Which unclutters the result:



While this works for me, I'm fairly sure that it's not correct overall.  In particular, I don't expect it to work when there are many series that cause the legend to span multiple lines.

Hopefully there's enough in this post to make it easy for someone else to generate a correct fix.

Source code:

// imports omitted for clarity
public class MyActivity extends ActionBarActivity {

  private final static Random rand = new Random();

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_my);
  }

  @Override
  protected void onResume() {
    super.onResume();
    Point p = new Point();
    p.x = getWindowManager().getDefaultDisplay().getWidth();
    p.y = getWindowManager().getDefaultDisplay().getHeight();
    int maxDim = p.x > p.y ? p.x : p.y;
    int fontSize = maxDim / 60;
    LinearLayout layout = (LinearLayout) findViewById(R.id.graph_surface);
    XYSeries series1 = new XYSeries("Series 1");
    setXYSeriesData(series1);
    XYSeries series2 = new XYSeries("Series 2");
    setXYSeriesData(series2);
    XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
    renderer.setLabelsTextSize(fontSize);
    renderer.setLegendTextSize(fontSize * 2);
    renderer.setShowLabels(true);
    renderer.setAxisTitleTextSize(fontSize);
    XYSeriesRenderer r = new XYSeriesRenderer();
    r.setColor(Color.RED);
    renderer.addSeriesRenderer(r);
    r = new XYSeriesRenderer();
    r.setColor(Color.RED);
    renderer.addSeriesRenderer(r);
    renderer.setXTitle("X Title");
    renderer.setYTitle("Y Title");
    XYMultipleSeriesDataset dataSet = new XYMultipleSeriesDataset();
    dataSet.addSeries(series1);
    dataSet.addSeries(series2);
    renderer.setYLabelsAngle(270);
    layout.addView(ChartFactory.getLineChartView(this, dataSet, renderer));
  }

  private void setXYSeriesData(XYSeries r) {
    int start = 1;
    int end = 10;
    int value = 0;
    for (int i = start; i < end; i++) {
      r.add(i, value);
      int change = rand.nextInt(15) - 7;
      value += change;
    }
  }
}

Sunday, September 7, 2014

Statistics paralysis ...

Technical people know all about statistics.  We focus on frames per second, or INSERTs per second or any number of other stats to tell us whether we did a good job with our programming or not.  It's rather amazing (to me) how much of our statistical tracking is about performance.  I suppose that's a side-effect of the perception by most of the world that the primary purpose of computers is to do things faster.  It might also be related to the fact that it's something that's easy to measure and communicate.  You don't often see technical people relaying stats about improved accuracy or improved quality as a result of software.  It happens, but not nearly as often as somethings per second are reported.

From a business side, people tend to worry about ROI-focused statistics.  Things like cost-per-click or number of page views for marketing, or profit/loss for the accountants.

Most of this isn't new.  If you see an old movie with a room full of workers at adding machines, they're probably compiling the statistics for their company.  What is relatively new to the business world is the speed and simplicity with which statistics can be collected and converted into a consumable form (such as graphs and charts).  Computers can collect data and turn it into statistics so fast that a lot of people don't even realize that there's a difference between data and statistics, or a process to converting.

I ran afoul of this last week, experimentally running some advertisements while at the same time doing some performance improvements to Idamu Caverns.  I found myself micro-profiling parts of the code until I ran out of ideas on how to make it faster, then spending a lot of time refreshing the statistics of the advertising system I was using to watch the page views and clicks change over time.

There were two big problems with this:

  1. The game was already fast enough.  Sure, it can always be faster, but the performance was no longer the most important thing I needed to work on, yet I'd become so focused on making the numbers smaller that I'd failed to notice that fact.
  2. Watching advertising statistics in time intervals less than 24 hours is almost a complete waste of time.

But I could do both of those things, so I did.  It wasn't until the next day when I was reviewing my work from the previous day and planning what to do next that I realized that I'd become paralyzed by the statistics, a slave to them perhaps.  Constantly tweaking ridiculous things and watching to see if the numbers improved.

It's not to say that super-tweaking is never good.  If you have a server that's hosting a large number of users and you can improve performance by 0.1% -- that could result in significant gains over time.  But that's a business justification.  Improving performance simply because you can is not productive, and that's what can happen when you look at the numbers without carefully considering what they mean.

I've seen this in large companies where there are people who's job it is, specifically, to look at the stats.  Since that's all they do, it's very easy for them to bog down others in their constant analysis of stats.  I think the solution to this is strong leadership within the company that specifically lays out time periods over which statistics are to be analyzed.  You see some of this.  For example, boards of directors will commonly want to see statistics on a quarterly basis, and don't want to be bothered with it more often than that.

On a personal level, it's an act of self-discipline (for me, at least).  I find myself doing it when the important tasks I have to do are unpleasant ones, but also spending too much time on it due to a failure to notice that things are already "good enough."  For me, I find that scheduling daily times when I go over my TODO list and prioritize it keeps me from getting too far off track.  But also, I sometimes allow myself to do the tasks that I enjoy, even if they aren't the most important.  It takes some of the sting out of doing the unpleasant ones.

Being both the worker and the boss is frequently a challenge for me.  I often joke that I don't get along with my boss at all.

Friday, September 5, 2014

Idamu Caverns Roadmap

Idamu Caverns is successful as a game, and I'm excited about where it's going.

Unfortunately, it's not getting there as fast as necessary to pay my bills.  It's likely that it will be a year or more before the game is generating sufficient income to support the time spent continuing development.

To avoid total failure, I'll be shifting my schedule a bit to focus on other projects that should provide more short-term income.  This means that I'll be cutting back releases of Idamu Caverns to every other week.  The current development road map is as follows:

  • September 15th: new item type: ring and a few magic rings to show off the new feature
  • September 29th: basic quest code and a single quest to show off the new feature
That's as far out as I'm planning at this time.  Based on the progress of other projects, as well as the reception of the September 15th and 29th releases, I'll plan October out later this month.  Unless something changes the releases will be on the 13th and 27th of October.

If you'd like to see Idamu Caverns development move faster, there are some things you can do:
  1. The most important thing is to spread the word.  The game doesn't have enough exposure yet, and advertising is expensive.  Like/Favorite my posts about the game and share them with anyone you know who might be interested.  Talk about the game on your blog or convince others to do so.  Post a positive review on the play store, or join the Google community and tell me what you don't like so I can improve it.
  2. If you can afford a few dollars a month, pitch in by becoming a Patron of the game.

Thank you to everyone who has supported the game so far.  I hope that you're enjoying playing it, and I'm looking forward to making it more enjoyable with each release.