Skip to content

Instantly share code, notes, and snippets.

@PotOfCoffee2Go
Last active October 31, 2020 14:28
Show Gist options
  • Save PotOfCoffee2Go/657ae23ec2939600941db6c20443fb13 to your computer and use it in GitHub Desktop.
Save PotOfCoffee2Go/657ae23ec2939600941db6c20443fb13 to your computer and use it in GitHub Desktop.
Documentation for observablehq.com game of life

Documentation for Game of Life page

Design

The classes that run the game has been more or less been updated to ES6 - and given the drive and will would integrate into a D3 representation of the automata. Since I do not have that drive or will - just interfaced to observablehq using the simplest methods possible.

ObservableHq runs Javascript class instances using a Web Worker. There are various ways of accessing the DOM, and for the DOM to have access to the class functions (such as 'onclick' functions of the buttons).

I decided to use the browser standard built-in object globalThis (a single line addition to the class constructor) of the 'Game' class which allows global access to the Game's functions. Thus, only the last instantiated (new Game(...)) is the active instance interfacing to the DOM. (btw - multiple instances of 'Game' in other contexts would be a 'layer').

Since there is only one DOM - can only have one active instance of Game - thus globalThis works nicely since it overwrites references to any previous instances, and the Garbage Collector will quietly dispose of them.

To insure the DOM is ready, the code checks for a known HTML element, when the element switches from undefined to defined the DOM is considered ready. Simple but relatively effective.

Due to 'Game' instance being the only interface recognized by the observablehq notebook - modifications to classes instantiated by the Game class will not be 're-loaded' automatically by observablehq. Basically, might have to refresh the page ;(

Docs and Documents classes (draft)

These two classes display the documentation that you are viewing right now!

The goals are:

  • Consolidate the documentation in one place
  • Navagation to organize documentation
  • Have a pleasing transition to show/hide the documention
  • Once shown, switch from document to document quickly
    • Minimal transitions
    • Display appropriate sub-menu (navsub)

Menu HTML

There are three div's with ids 'navbar', 'navsub' and 'docs'. The transitions are performed by CSS in the Web Page style() function in an ObservableHq cell below, the styles can be identified by the comment /* Document display */. The transitions occur when the Docs class adds/removes classes from the <div> classList.

A sample of the ObservableHq cell you are currently viewing should look something like:

html `
  <div id="navbar">
    <button onclick="docs.toggleDoc(this);" id="docs-show-btn"> Intro </button>
    <button onclick="docs.displayDoc('usagebar','usage');"> Usage </button>
    <button onclick="docs.displayDoc('designbar','design');"> Design </button>
    <button onclick="docs.displayDoc('nobar','why');"> Why? </button>
    <div id="navsub"></div>
  </div>
  <div id="docs"></div>
`;

The Intro button is special in that it changes to Hide when the documention is being displayed. The docs-show-btn id is used to fix the width of the button so it doesn't change when the text changes.

Note the Intro button calls docs.toggleDoc() passing a reference to itself. Intro is the only button that uses docs.toggleDoc. Notice all the others call docs.displayDoc('sub-menu-name','document-name');. Also the text Intro and Hide are hard-coded in the Docs class.

The Why? button calls docs.displayDoc with the 'sub-menu-name' of 'nobar' - which hides the sub-menu.

Docs class

Docs controls the DOM to show/hide the documentation and creates an instance of Documents which reads the documentation Markdown files from a GitHub Gist.

Docs class uses [globalThis][1] globalThis.docs = this; in the constructor so that the DOM can reference it in the onclick="..." attribute. If another (last) instance is created, it becomes the current docs instance - replacing the previous one (old one will be Garbage Collected). If/whan a new Docs is instantiated the documents are re-fetch from GitHub, which is a easy way to update documents to the most recent commit.

The targetBlank() function adds target="_blank" to all <a>nchor tags. This allows the reader to click a link without refreshing the page thus wiping out the currently running cellular automaton! - which would be a sad thing ;(

Other than that nothing to special about it. The timing of the transitions was by trial and error, so probably a lot of improvement could be done if someone actually designed a coordinated start/duration/stop/delay between the div elements.

Documents class

Documents is instantiated by Docs. This class contains all the Markdown documents which are fetch from a [GitHub Gist][2].

The following code fragments from the Documents class illustrates how the Gist URL and filenames in the Gist are accessed (.md is automatically appened to each filename.

init() {

    this.gist = 'https://gist.githubusercontent.com/' +
      'PotOfCoffee2Go/657ae23ec2939600941db6c20443fb13/' +
      'raw/';
    this.mdFileList = [
      'intro',
      'usage',  'usage_controls', 'usage_pattern', 'usage_world', 'usage_data',
      'design', 'design_document', 'design_game', 'design_display', 'design_pattern',
                'design_agent', 'design_cell', 'design_world',  
      'why'];

}

In this function the sub-menus HTML are declared.

navsubsHtml() {

  // --- design sub-menu
  this.designbar = html `
  <span>
    Design: 
    <button onclick="docs.displayDoc('designbar','design_document');">Docs</button>
    <button onclick="docs.displayDoc('designbar','design_game');">Game</button>
    <button onclick="docs.displayDoc('designbar','design_display');">Display</button>
    <button onclick="docs.displayDoc('designbar','design_pattern');">Pattern</button>
    <button onclick="docs.displayDoc('designbar','design_world');">World</button>
    <button onclick="docs.displayDoc('designbar','design_cell');">Cell</button>
    <button onclick="docs.displayDoc('designbar','design_agent');">Agent</button>
  </span>
  `;

You can also add documents directly (without the need to fetch from the Gist).

  // A document named 'buttons'
  this.buttons = md `
  Buttons - There are three screens used to control and display the
  cellular automata. The World screen, the Pattern screen, and the Data
  screen. The World screen has all of the controls and displays info on
  the current generation. 
  `;

}

Note! the names of files, documents, and sub-menus must be unique as all of them are in the same object!

Introduction

Visit Conway's Game of Life on wikipedia for information about the game itself.

This implementation is very basic and useful for playing around with smaller patterns of gliders, spaceships, oscillators, guns and such. Recommend golly for any serious cellular automata studies.

As a tribute to Mr. Conway, I wish to present the Game of Life in the spirit of the 1970's when he introduced the world to cellular automata. The display is totally text based - a flashback to a simpler, mouseless era of keyboards without a 'cmd' key, fuzzy CRT screens, and when Tiny Pascal was cutting edge. Visualizations were restricted by the hardware but still engaging when done well.

My first attempt at programming a cellular automaton was in BASIC at Kiewit Computation Center, more years ago than I wish to admit. But wasn't until Tiny Pascal was available on the Radio Shack TRS-80 that actually/kinda got one to work.

Thus this notebook is not very '@observablehq'-ish as I just copy/paste(d) the classes from a web page I had created a quite while back and figured would leave well enough alone. Check out the Design for details.

Mr. Conway, we miss you and hopefully you would approve.

{
"name": "Game-of-Life-Docs",
"version": "1.0.0",
"description": "Cellular automata Conway's Game of Life",
"main": "_Observe_GOL.md",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": ["cellular automata", "Game of Life"],
"author": "PotOfCoffee2Go <https://github.com/PotOfCoffee2Go>",
"license": "ISC"
}

Usage

There are two input and two output screens to control and display the cellular automata.

The input screens allow modiications and control of the automata generations.

  • Control screen
  • Pattern screen

The output screens represent the currrent generation of the automaton.

  • World screen
  • Data screen

Control Screen

The control screen has a set of buttons and a slider that controls activities on the world screen.

  • Clear
    • Sets all cells to dead - all statistics to zero
  • Start/Stop
    • Toggles starting and stopping generations
  • Step
    • Perform a single generation
  • (slider)
    • Delay (speed) between generations
  • Edit
    • Set cell alive/dead using mouse
  • Random
    • Fill world with random set of alive cells

Why this notebook?

One of the most striking early visualizations was to watch the complex patterns generated by the most simpliest rules of the Game of Life, which anyone with an interest in programmming could produce - and watch with amazement as an intricate hereto unseen world of life and death unfolded before them on the screen.

That spirit to describe and portray the complex in visually exciting and yet simple understandable terms. I am amazed and awestruck at some of the visualizations that are producded by people using D3 on ObservableHq. It kindles the excitment of the first visualizations I saw back in the early years of the personal computer.

Is enlightening to see the quest to expand knowledge, challenge minds, and engage curiousity lives on today as much as it did 50+ years ago.

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