Skip to content

Instantly share code, notes, and snippets.

@misterussell
Created March 27, 2018 14:12
Show Gist options
  • Save misterussell/4eadd04299f8189abebf79569ede9fce to your computer and use it in GitHub Desktop.
Save misterussell/4eadd04299f8189abebf79569ede9fce to your computer and use it in GitHub Desktop.
3 26 18 Blog Post

Optimizing Conway's Game of Life in JavaScript - Part IV

Feature Addition: Tracking

Content with my initial product, albeit a few bugs, I'm switching gears away from the UI/UX to work on one of the initial features that I wanted to integrate into this project. DASHBOARDS. Well, the logic behind them at least. Have to start somewhere!

// image here

Somewhat unimportant information that I'm choosing to write it down.

I do a lot of my initial building at work, outside of my actual dev environment. This is both a challenge and an interesting way of doing things. One of the main reasons for this is because I work on a Mac at home and a PC at work and prioritize writing code, rather than researching how to replicate my dev environment on another OS. Priorities change, but until this one does, I will continue to work this way.

  1. I work with attention towards programming functionally. By not having access/deciding not to replicate the exact state of my app outside of the project I have to be very mindful of what I do have access to. I'm not grabbing random parts of state, or relying too much on the functions on the view to do my calculations.
  2. I effortfully refactor. My initial test cases don't necessarily follow the way I have data organized in my app state. I allow myself time to make judgements on what I am integrating. This happens on two levels. One level is addressing whether or not the data has a use. Frivolous data only complicates state. The second level is whether or not the structure of the data I am integrating is organized in an efficient way. Am I doing too much iteration, is there a way to access nested data more cleanly? Am I keeping functions simple and relying on as few possible variables as possible (or a single object).
  3. I do not get distracted by non-priority functionality. Bugs don't exist when they aren't in the code! If I'm working on a specific block of functionality in my non-dev environment I don't have the option of toying around with Modal display delays, or fiddling with my Bootstrap component styling. I can get right to the root of what I'm trying to tackle.
  4. I loose out on early stage unit tests for the new functions. I'm testing at the console level frequently when getting into the initial code. This creates more work, but I do get more familiar with the structure of the data when I bring it in. Thus writing test cases becomes faster when integrating.
  5. The sleeper awakens. Uncluttered code leaves a lot to be imagined! I keep it simple and I'm not trapped by my previous unnecessary complications. Sometimes this even means I end up fixing those. I am a giant Dune nerd.

Choosing What to Track

As the game is rather quick-moving and doesn't leave a lot of opportunity for creative user actions I'm focusing on data. Data about the cells and the game board as a whole, not user-centric data. What cells people pick to start the game with does not interest me, yet. A few examples of what I am tracking:

  • Which cell/s had the longest periods of uninterrupted life
  • Which cell/s regenerated the most?
  • Did a single cell regenerating cause a noticeable effect on subsequent generations?
  • Were some areas of the game board noticeably more active than others?

Initial Setup for Tracking

For my pre-integration data I am utilizing an array of objects to represent the different generations over time. My app only tracks one object replacing the object with the next iteration of the object. The intention behind this is to curb the amount of data I'm storing should the game last for a few hundred iterations.

I am tracking with two objects.

  1. An object to track the singular cell's activity.
let cellTracking = {}
  1. An object to track the group of cells as a whole. This is currently comprised of only 2 arrays.
let lifeBoardTracking = {
  generationLifeSums: [],
  generationDeathSums: []
};

The data is generated with a single forEach loop over the object arrays. I predict that I will move away from this during integration as the data will be tracked by state so updates will be made step by step, not recursively. I'm not going to discuss this iteration as it is a standard process. See link for full code at bottom of the article if you need an example of how I've done this.

The bulk of my code is handled in this loop. I'm using destructing to define variables that I return to the main cellTracking object. In this way I take what already existed and return a copy with all previous data intact.

return cellTracking[key] = {
  cellHistory,
  cellStateSum,
  count,
  averageLife: cellStateSum/count,
  sustainedPeriods,
  births,
  deaths
};

An example of one of our hashed cells with their cell specific information below. This gives me enough information to start plotting data onto charts. Now just to decide if these should end up on an hide-able modal, or keep things more accessible and have the dashboard persist until another game is started.

92917664: {
  averageLife = 0.466666666666667,
  births: 3,
  cellHistory: [1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0],
  cellStateSum: 7,
  count: 15,
  deaths: 4,
  sustainedPeriods: {
    death: [3, 1, 2, 2],
    life: [1, 3, 2, 1]
  }
};

This is just the start of the data. I also intend to use tracking to target looped patterns or patterns that become stale and unchanged. I predict that I will persist these sorts of patterns so that they can looked up later, adding a more historical synopsis of app use that goes beyond the immediate play/stat format I'm starting with.

See the full code here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment