Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .cloudflare/public/_app/version.json

This file was deleted.

Binary file removed .cloudflare/public/logo.png
Binary file not shown.
20 changes: 0 additions & 20 deletions .eslintrc.cjs

This file was deleted.

31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: CI

on:
push:
branches: [master]
pull_request:

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.11

- run: bun install --frozen-lockfile

# The engine builds first: the CLI and the components import it from
# `dist`, so nothing else can be checked until it exists.
- run: bun run build:engine
- run: bun run typecheck
- run: bun run test
- run: bun run build:cli
- run: bun run build:web

# The Android app is the web build plus a Capacitor copy step. Assembling
# the APK needs the Android SDK; verifying the copy does not.
- run: bunx cap sync android
working-directory: apps/android
17 changes: 13 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
.DS_Store
node_modules
/build
/dist
/.svelte-kit
build
dist
.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
.cloudflare/
.wappler/
.wappler/
*.tsbuildinfo

# Android / Capacitor build output
apps/android/android/app/build
apps/android/android/build
apps/android/android/.gradle
apps/android/android/local.properties
apps/android/android/app/src/main/assets/public
apps/android/android/app/src/main/assets/capacitor.config.json
101 changes: 83 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,99 @@
<img alt="GitHub Discussions"
src="https://img.shields.io/github/discussions/vtempest/Automate-Automata" />
</a>
<a href="https://www.npmjs.com/package/complexity-automata">
<img alt="npm" src="https://img.shields.io/npm/v/complexity-automata?label=complexity-automata" />
</a>
<a href="https://github.com/vtempest/Automate-Automata/pulse" alt="Activity">
<img src="https://img.shields.io/github/commit-activity/m/vtempest/Automate-Automata" />
</a>
<img src="https://img.shields.io/github/last-commit/vtempest/Automate-Automata.svg?style=flat-square" alt="GitHub last commit" />
</p>
<p align="center">
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square"
alt="PRs Welcome" />
<a href="https://codespaces.new/vtempest/Automate-Automata">
<img src="https://github.com/codespaces/badge.svg" width="150" height="20" />
</a>
</p>
<h3>
<a href="https://automata-game-of-life.vtempest.workers.dev"> 🚀 Demo</a>
<a href="https://play.google.com/store/apps/details?id=com.vtempest.automata"> 🚀 Android App</a>
</p>

<h3 align="center">
<a href="https://automata-game-of-life.vtempest.workers.dev">🚀 Web app</a> ·
<a href="https://play.google.com/store/apps/details?id=com.vtempest.automata">📱 Android app</a> ·
<code>npx complexity-automata-cli</code>
</h3>

## Complexity Automata

Conway's Game of Life, and the 262,143 other universes that share its shape.

A cell is **born** when it has exactly three live neighbours and **survives**
with two or three. That is the entire rule. The gliders that walk across the
screen, the gun that fires them forever, and the fractals that copy themselves
all follow from it — and changing two numbers gives you a different universe
with its own physics.

- **Draw** patterns into a running grid — gliders, guns, oscillators, fractal seeds
- **Colour inheritance**: cells take the average colour of the cells that made
them, so colonies drift apart and collisions fade into blends
- **Save universes** and reopen, share or download them; the same file opens in
the web app, the terminal, and other Life software
- **Automatic mode** tours random rules, moving on whenever one stops evolving
- **Twelve demos**, from Conway's to Fredkin's fractal replicator

Runs the same on the web, on Android, and in a terminal.

## What is in here

| | | |
| --- | --- | --- |
| [`packages/complexity-automata`](packages/complexity-automata) | `complexity-automata` | The engine. Rules, grid, colour, patterns, demos, saving. No DOM, no dependencies. |
| [`packages/complexity-automata-cli`](packages/complexity-automata-cli) | `complexity-automata-cli` | The terminal animation, in truecolor half-blocks. |
| [`packages/complexity-automata-svelte`](packages/complexity-automata-svelte) | `complexity-automata-svelte` | Svelte components: canvas view, rule switches, demo picker, saved universes. |
| [`apps/web`](apps/web) | | The web app — SvelteKit, deployed to Cloudflare. |
| [`apps/android`](apps/android) | | The Android app — the same build, wrapped by Capacitor. |

## Try it

```bash
# In a terminal
npx complexity-automata-cli # Conway's, with gliders and a gun
npx complexity-automata-cli fractal # a Sierpinski triangle from one cell
npx complexity-automata-cli --auto # a tour of random universes

# In your own code
npm i complexity-automata
```

## Automate Automata: "Game of Life" App
```js
import { Automata } from 'complexity-automata';

* Custom rule sets, common patterns, and color mixing to produce artistic fades
const life = new Automata({ width: 80, height: 40, rule: 'B3/S23' });
life.stampCentered('Glider Gun');
life.run(120);
console.log(life.toString());
```

* Automated mode - randomize rule set changes & patterns and watch life unfold...
## Developing

* developed in 2014 and 10+ years as Android app with Javascript, rewriten in Svelte
Bun workspaces and no build orchestration to learn:

```bash
bun install
bun run build # engine → CLI → web
bun run dev # the web app at localhost:5173
bun run cli # the terminal animation from source
bun run test # every package's suite
bun run typecheck
bun run android # build the web app and assemble the APK
bun run deploy # wrangler deploy from apps/web
```

### Further Research
The engine has no dependencies and is tested on its own (`cd
packages/complexity-automata && bun test`); the CLI and the components are thin
layers over it. If a change is about *what the automata do*, it belongs in the
engine, with a test.

## Further Research

* Johnston, Nathaniel, and Greene, Dave (2022). "Conway's Game of Life". https://conwaylife.com/book/#rle_files

Expand All @@ -50,21 +114,22 @@

* Wolfram, Stephen (2013). "Talking about the Computational Future at SXSW 2013", https://writings.stephenwolfram.com/2013/03/talking-about-the-computational-future-at-sxsw-2013/

#### Excerpt:
#### Excerpt:

> By the way, it’s bizarre how few people work on this. Because I’m sure that, just like cloning, there’s just going to be a wacky procedure that makes it possible—and once we know it, we’re just going to be able to do it quite routinely, and it’s going to be societally very important. But in the end, we want to solve the problem of keeping all the complexity that is a human running indefinitely. There are some fascinating basic science problems here. Connected to concepts like computational irreducibility, and a bit to the traditional halting problem. But I have no doubt that eventually it’ll be solved, and we’ll achieve effective human immortality. And when that happens I expect it’ll be the single biggest discontinuity in human history.
>
>
> Cellular automata
>
>
> You know, as one thinks about such things, one can’t help wondering about the general future of the human condition. And here’s something someone like me definitely thinks about. I’m spending my life trying to automate things. Trying to make it possible to do automatically with computation things that humans used to have to do themselves.
>
>
> Now, if we look at the arc of human history, the biggest systematic change through time is the arrival of more and more technology, and the automation of more and more kinds of tasks. So here’s a question: what if we succeed in automating everything? What will happen then? What will the humans do? There’s an ultimate—almost philosophical—version of this question. And there’s also a practical next-few-decades version.
>
>
> Let’s start with the ultimate version. As we go on and build more and more technology, what will the end point be? We might assume that we could somehow go on forever, achieving more and more. But the Principle of Computational Equivalence tells us that we cannot. One we have reached a certain level, everything is already in a sense possible. And even though our current engineering has not yet reached this point, the Principle of Computational Equivalence also tells us that this maximal level of computational sophistication is not particularly rare. Indeed it happens in many places in the physical world, as well as in systems like simple cellular automata.
>
>
> Cellular automata
>
>
> And it’s not too hard to see that as we improve our technology, getting down to the smallest scales, and removing everything that seems redundant, that we might wind up with something that looks just like a physical process that already happens in nature. So does this mean that in the ultimate future, with all that great automation and technology, all we’ll achieve is just to produce something that’s indistinguishable from zillions of things that already exist in nature?
>
>
> Wolfram, Stephen (2013). "Talking about the Computational Future at SXSW 2013", https://writings.stephenwolfram.com/2013/03/talking-about-the-computational-future-at-sxsw-2013/

MIT · developed in 2014, ten years as an Android app, rewritten in Svelte in 2024, split into engine, apps and CLI in 2025.
3 changes: 0 additions & 3 deletions android/capacitor.settings.gradle

This file was deleted.

50 changes: 50 additions & 0 deletions apps/android/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Android app

The web app in a WebView, wrapped by [Capacitor](https://capacitorjs.com).
There is no Android-specific UI: `capacitor.config.ts` points `webDir` at
`../web/build`, so the app ships exactly the bundle Cloudflare serves.

Published as [com.vtempest.automata](https://play.google.com/store/apps/details?id=com.vtempest.automata).

## Build

```bash
bun run sync # build the web app, then copy it into the Android project
bun run build # sync, then assemble a debug APK
bun run release # sync, then bundle a signed release AAB
bun run open # open the project in Android Studio
```

`bun run build` needs a JDK and the Android SDK (`ANDROID_HOME`); `bun run sync`
needs neither, so CI can verify the copy step on its own.

Output: `android/app/build/outputs/apk/debug/app-debug.apk`.

## What is checked in

The Gradle project under `android/`, the launcher icons and splash screens under
`resources/`, and the Capacitor config. The synced web assets
(`android/app/src/main/assets/public`) are build output and are ignored.

## Changing the app

- **The UI** is [`packages/complexity-automata-svelte`](../../packages/complexity-automata-svelte)
- **The version** is `versionCode`/`versionName` in `android/app/build.gradle`
- **The name** is `app_name` in `android/app/src/main/res/values/strings.xml`
- **Icons and splash** are regenerated from `resources/` with
`bunx @capacitor/assets generate --android`

The page is laid out for a phone: the controls sit inside the safe area, the
grid takes the rest, and `overscroll-behavior: none` keeps pull-to-refresh from
fighting with drawing on the grid.

## First-time setup

The project was created with:

```bash
bun i @capacitor/core @capacitor/android
bun i -D @capacitor/cli
bunx cap init
bunx cap add android
```
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ android {
applicationId "com.vtempest.automata"
minSdkVersion 26
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 3
versionName "2.0.1"
versionCode 4
versionName "3.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_name">Automated Automata</string>
<string name="title_activity_main">Automated Automata</string>
<string name="app_name">Complexity Automata</string>
<string name="title_activity_main">Complexity Automata</string>
<string name="package_name">com.vtempest.automata</string>
<string name="custom_url_scheme">com.vtempest.automata</string>
</resources>
File renamed without changes.
3 changes: 3 additions & 0 deletions apps/android/android/capacitor.settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../../../node_modules/.bun/@[email protected]+bbf93636f05d5b82/node_modules/@capacitor/android/capacitor')
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
23 changes: 23 additions & 0 deletions apps/android/capacitor.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { CapacitorConfig } from '@capacitor/cli';

/**
* The Android app is the web app in a WebView. `webDir` points at the
* SvelteKit static build one directory over, so `bun run build` here builds the
* web app and syncs the very same files Cloudflare serves.
*/
const config: CapacitorConfig = {
appId: 'com.vtempest.automata',
appName: 'Complexity Automata',
webDir: '../web/build',
android: {
// The grid is drawn on a dark canvas; matching the shell avoids a white
// flash between the splash screen and the first frame.
backgroundColor: '#080a10',
allowMixedContent: false,
},
server: {
androidScheme: 'https',
},
};

export default config;
22 changes: 22 additions & 0 deletions apps/android/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "@automate-automata/android",
"private": true,
"version": "3.0.0",
"description": "Capacitor wrapper that ships the web app as the Android app.",
"type": "module",
"scripts": {
"build": "bun run sync && bun run assemble",
"sync": "cd ../web && bun run build && cd ../android && cap sync android",
"assemble": "cd android && ./gradlew assembleDebug",
"release": "cd android && ./gradlew bundleRelease",
"open": "cap open android"
},
"dependencies": {
"@capacitor/android": "^6.1.1",
"@capacitor/core": "^6.1.1"
},
"devDependencies": {
"@capacitor/cli": "^6.1.1",
"typescript": "^5.5.4"
}
}
File renamed without changes
File renamed without changes
File renamed without changes
31 changes: 31 additions & 0 deletions apps/web/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Web app

SvelteKit shell around [`complexity-automata-svelte`](../../packages/complexity-automata-svelte).
Everything on screen is the `AutomataApp` component; this app only supplies the
page, the metadata and the URL handling.

```bash
bun run dev # localhost:5173
bun run build # static bundle in build/
bun run preview
bun run deploy # wrangler deploy
```

## URL parameters

| | |
| --- | --- |
| `?demo=fractal` | open straight into a demo |
| `?intro=0` | skip the intro screen |
| `?cell=6` | CSS pixels per cell — bigger cells, smaller universe |
| `?u=…` | open a shared universe |

## How it is built

`adapter-static` with an `index.html` fallback, rendered entirely on the client
(`ssr = false` — the app is a canvas, so there is nothing useful to render on a
server). That single `build/` directory is what Cloudflare serves *and* what
Capacitor copies into the Android app, so the two can never drift apart.

Because rendering is client-side, the page title and social metadata live in
`src/app.html` rather than in a `<svelte:head>`.
Loading
Loading