INT version of gliderdb

For discussion of other cellular automata.
Post Reply
User avatar
MDA
Posts: 294
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

INT version of gliderdb

Post by MDA »

I recently became interested in gliderdb and how it aimed to be a collection of all known spaceship speeds in outer-totalistic 2-state CA. I also learned about 5S, which only collected the smallest known example for every speed. Then, I read about Project Omniraumschiff, which was meant to be a web version of gliderdb, kept up-to-date by the community. However, it was never implemented and it only focused on outer-totalistic CA.

I am now planning to combine the two and develop a database across all spaceship-supporting 2-state INT rules (including B0) listing the smallest spaceships known for every speed (except for adjustable speeds). I will name the database “GlidINT” and it will be on my GitHub. Rules:

Every spaceship shall have an RLE associated with it, oriented to move between north and northwest. The phase used should have minimum population and bounding box.

Every spaceship shall be listed by its population, speed, minrule, and maxrule.

Whenever a new (smaller) spaceship is discovered, it will replace the older spaceship in all the rules where it works.

“Oversized” spaceships (whose minpop phases are larger than 400x400) shall be added as compressed files.

All scripts necessary for the database will be stored in a folder called “src”.

All discovery information (name, date, discoverer, and program used) should be added to attribution.txt, if known. Names should be written with the last name first (such as “Callahan, Paul”). Dates should follow YYYY-MM-DD or YYYY-MM or just YYYY.

Attribution format:

Code: Select all

Glider    Guy, Richard    manual    1969
Format:

Code: Select all

{minpop}P{period}H{dx}V{dy} {minrule} - {maxrule} {headerless RLE}
This project will obviously need much attention and care, but I’m confident that it will work. I will also need script help. The script this project needs should:

- Identify patterns

- Check whether they are spaceships

- Orient the resulting spaceship correctly

- Return a line which can be pasted into the database.

Link: https://github.com/HavenMDA/GlidINT
Last edited by MDA on July 11th, 2026, 1:44 pm, edited 2 times in total.
My website (a database of Life objects, WIP).
GlidINT has been released!
Currently working on a forum system for my website.

Code: Select all

x = 5, y = 17, rule = B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-akHistory
C$2C$.2C$2C11$.3D$3.D$2.3D!
[[ STOP 72 ]]
User avatar
NNlk05
Posts: 779
Joined: January 14th, 2026, 8:42 pm
Location: Exploring in the Jungle of the INT Rulespace
Contact:

Re: INT version of gliderdb

Post by NNlk05 »

MDA wrote: July 11th, 2026, 5:08 am *snip*
- Return a line which can be pasted into the database.
Why not run it on a server? You'll need one anyways for the front-end, and free serverless platforms are good enough.
Feci quod potui, faciant meliora potentes.

Code: Select all

x = 10, y = 3, rule = B34twz/S23
b2o4b2o$obo4bobo$2bo4bo!
[[ AUTOSTART AUTOHIDEGUI TRACK 0 -47/270 ZOOM 4 GPS 45 STEP 3 THEME BOOK ]]
https://nnlk05.github.io

=3
User avatar
MDA
Posts: 294
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

Re: INT version of gliderdb

Post by MDA »

NNlk05 wrote: July 11th, 2026, 5:13 am
[…]

Why not run it on a server? You'll need one anyways for the front-end, and free serverless platforms are good enough.
I don’t know how. Maybe you can do that part and I’ll work on a script to clone the repository, check for the presence of a specific spaceship, compare it to the submitted one, and add it if it’s smaller.
My website (a database of Life objects, WIP).
GlidINT has been released!
Currently working on a forum system for my website.

Code: Select all

x = 5, y = 17, rule = B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-akHistory
C$2C$.2C$2C11$.3D$3.D$2.3D!
[[ STOP 72 ]]
User avatar
NNlk05
Posts: 779
Joined: January 14th, 2026, 8:42 pm
Location: Exploring in the Jungle of the INT Rulespace
Contact:

Re: INT version of gliderdb

Post by NNlk05 »

MDA wrote: July 11th, 2026, 5:28 am I don’t know how. Maybe you can do that part and I’ll work on a script to clone the repository, check for the presence of a specific spaceship, compare it to the submitted one, and add it if it’s smaller.
I don't have access to my various accounts until about 2026-8-10, so I won't be able to work on this until then.
Feci quod potui, faciant meliora potentes.

Code: Select all

x = 10, y = 3, rule = B34twz/S23
b2o4b2o$obo4bobo$2bo4bo!
[[ AUTOSTART AUTOHIDEGUI TRACK 0 -47/270 ZOOM 4 GPS 45 STEP 3 THEME BOOK ]]
https://nnlk05.github.io

=3
User avatar
PK22
Posts: 626
Joined: January 25th, 2025, 11:38 am
Location: United Kingdom
Contact:

Re: INT version of gliderdb

Post by PK22 »

MDA wrote: July 11th, 2026, 5:08 am (...)

This project will obviously need much attention and care, but I’m confident that it will work. I will also need script help. The script this project needs should:

- Identify patterns

- Check whether they are spaceships

- Orient the resulting spaceship correctly

- Return a line which can be pasted into the database.

Link: https://github.com/HavenMDA/GlidINT
Questions:
1. What language should the script be written in? Using TypeScript and lifeweb would probably be ideal, since lifeweb can easily find minrule and maxrule, and might allow some processing to be shifted client-side if you want to go with a server frontend. Lifelib (which can be used with either Python or C++) has several problems for this idea, namely needing recompilation for new rules (which can take >20 seconds in the case of Python, so finding minrule and maxrule could take over half an hour), and requiring x86 architecture to run. I have a Python package which can simulate patterns without requiring recompilation for new rules, but it might be too slow for some of the more chaotic spaceships. (EDIT: I could also do a mix of C++ and Python, with simulation in C++ and the database processing in Python).

2. Under what circumstances should a line be replaced? You stated that the format is:

Code: Select all

{minpop}P{period}H{dx}V{dy} {minrule} - {maxrule} {headerless RLE}
Should a ship always replace the old one if it has a lower minpop and matches the period and displacement? If not, what variables need to match to force replacement?
User:PK22
Learning miscellaneous programming languages.
User avatar
speedydelete
Posts: 129
Joined: October 7th, 2025, 9:44 pm
Contact:

Re: INT version of gliderdb

Post by speedydelete »

This doesn't seem possible to me. The compressed size of the 5S database for INT is more than 30 MiB, this database is likely to be thousands of times larger than it.

EPE can easily give thousands of ships in just a few minutes with the right commands, which would quickly fill up the database.
I manage the 5S project, which collects all known spaceship speeds in certain rulespaces.
User avatar
MDA
Posts: 294
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

Re: INT version of gliderdb

Post by MDA »

PK22 wrote: July 11th, 2026, 8:00 am
[…]

Questions:
1. What language should the script be written in? Using TypeScript and lifeweb would probably be ideal, since lifeweb can easily find minrule and maxrule, and might allow some processing to be shifted client-side if you want to go with a server frontend. Lifelib (which can be used with either Python or C++) has several problems for this idea, namely needing recompilation for new rules (which can take >20 seconds in the case of Python, so finding minrule and maxrule could take over half an hour), and requiring x86 architecture to run. I have a Python package which can simulate patterns without requiring recompilation for new rules, but it might be too slow for some of the more chaotic spaceships. (EDIT: I could also do a mix of C++ and Python, with simulation in C++ and the database processing in Python).
It doesn’t matter much what language you choose to write it in. You could write it in Python because of its vast library of helpful functions, at the cost of being an interpreted language, and thus, slower to run. You could also use C/C++ which is compiled, and thus faster, but at the cost of having to define almost all helpful functions yourself. Using lifelib is also not very advantageous, because, as you said, it has to be recompiled every time a rule is changed, so, in my opinion, the fastest solution is to write a custom C/C++ program for pattern analysis with a Python frontend.
PK22 wrote: July 11th, 2026, 8:00 am
2. Under what circumstances should a line be replaced? You stated that the format is:

Code: Select all

{minpop}P{period}H{dx}V{dy} {minrule} - {maxrule} {headerless RLE}
Should a ship always replace the old one if it has a lower minpop and matches the period and displacement? If not, what variables need to match to force replacement?
A spaceship in the database should only be fully replaced if

- The unsimplified displacement and period are the same in both spaceships

- The older spaceship has a higher min-population (or, if both minpops are equal, measure bounding-box)

- The new spaceship works in the older spaceship’s minrule and maxrule

Alternatively, if, by “replace”, you meant partial replacement, then spaceships should be replaced if

- The unsimplified displacement and period are the same in both spaceships

- The older spaceship has a higher min-population (again, if both minpops are equal, measure bounding-box)
speedydelete wrote: July 11th, 2026, 10:19 am This doesn't seem possible to me. The compressed size of the 5S database for INT is more than 30 MiB, this database is likely to be thousands of times larger than it.

EPE can easily give thousands of ships in just a few minutes with the right commands, which would quickly fill up the database.
As for your objection, speedydelete, both points are valid, but I think this problem is inevitable. I will likely have to rethink my formatting decisions once we get there. Perhaps a rule stating that rules with spaceships found by EPE or similar tools should not be added unless at least one spaceship is known in such rules found without EPE or other similar programs.

Edit (July 11th, 2026, 6:42 PM): Replying to both PK22 and speedydelete, a good rule of thumb to memorize these rules and how to apply them is that GlidINT only collects one spaceship for every known non-adjustable speed in a given rule.

Edit (July 11th, 2026, 6:56 PM): Actually, on second thought, I believe adjustable spaceships should be included in the GlidINT database. However, the attribution text file should state that the speed is adjustable and the formula defining the speeds which can be achieved (e.g. c/n or (n, n-1)c/(2 * (2n - 1))). The common RLE file should contain only the smallest member of the adjustable family and it should also mention the range of the adjustable family, not the individual spaceship included in the database.
My website (a database of Life objects, WIP).
GlidINT has been released!
Currently working on a forum system for my website.

Code: Select all

x = 5, y = 17, rule = B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-akHistory
C$2C$.2C$2C11$.3D$3.D$2.3D!
[[ STOP 72 ]]
User avatar
speedydelete
Posts: 129
Joined: October 7th, 2025, 9:44 pm
Contact:

Re: INT version of gliderdb

Post by speedydelete »

MDA wrote: July 11th, 2026, 1:38 pm
speedydelete wrote: July 11th, 2026, 10:19 am This doesn't seem possible to me. The compressed size of the 5S database for INT is more than 30 MiB, this database is likely to be thousands of times larger than it.

EPE can easily give thousands of ships in just a few minutes with the right commands, which would quickly fill up the database.
As for your objection, speedydelete, both points are valid, but I think this problem is inevitable. I will likely have to rethink my formatting decisions once we get there. Perhaps a rule stating that rules with spaceships found by EPE or similar tools should not be added unless at least one spaceship is known in such rules found without EPE or other similar programs.
This still doesn't fix the problem. You could write a script that runs qfind on random rules looking for endemic c/2o's, and submitting them once it finds them. Also, what's the point of maintaining this database if the multi-rule search programs aren't allowed?
I manage the 5S project, which collects all known spaceship speeds in certain rulespaces.
User avatar
MDA
Posts: 294
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

Re: INT version of gliderdb

Post by MDA »

speedydelete wrote: July 12th, 2026, 10:33 am
MDA wrote: July 11th, 2026, 1:38 pm
[…]

As for your objection, speedydelete, both points are valid, but I think this problem is inevitable. I will likely have to rethink my formatting decisions once we get there. Perhaps a rule stating that rules with spaceships found by EPE or similar tools should not be added unless at least one spaceship is known in such rules found without EPE or other similar programs.
This still doesn't fix the problem. You could write a script that runs qfind on random rules looking for endemic c/2o's, and submitting them once it finds them. Also, what's the point of maintaining this database if the multi-rule search programs aren't allowed?
I still haven’t finalized much regarding this project, so there’s still some wiggle room regarding what kinds of spaceships or rules are/aren’t allowed. This is meant to be a community project, so if you disagree with my assertion that “rules with spaceships found by EPE or similar tools should not be added unless at least one spaceship is known in such rules found without EPE or other similar programs”, we can decide to remove that rule. Also, keep in mind that since for a c/2o spaceship to be isotropically endemic, it has to be high-period or high-population, if you decide to “write a script that runs qfind on random rules looking for endemic c/2o’s, and submitting them once it finds them”, the database should mostly reject them unless no other c/2os are known if they’re large in population. Alternatively, if they’re large in period, they could be quite hard to find with qfind.

Perhaps we can implement a prototype version of GlidINT which only accepts spaceships which work in at least one rule at most 10 isotropic transitions from Life? This could help us understand whether or not your arguments hold in this simpler case before transitioning to the whole of the (spaceship-supporting) INT rulespace.

A restriction which could be added is that B0 rules with S8 are not allowed because they are always complementary to some rule with neither of those conditions and thus, any spaceship submitted in those rules is always present in the complement version (no B0 or S8).

Edit (July 13th, 2026, 12:08 PM): I’ve come up with another idea. Instead of hosting the database on GitHub, the repository will contain only the necessary scripts and the database itself will be hosted on my website.

Edit (July 15th, 2026, 2:16 PM): Retracted last edit, replaced with this message.

Edit (July 16th, 2026, 10:05 AM): GlidINT should be up and running by the beginning of August.
My website (a database of Life objects, WIP).
GlidINT has been released!
Currently working on a forum system for my website.

Code: Select all

x = 5, y = 17, rule = B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-akHistory
C$2C$.2C$2C11$.3D$3.D$2.3D!
[[ STOP 72 ]]
User avatar
MDA
Posts: 294
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

Re: INT version of gliderdb

Post by MDA »

I am expecting to open GlidINT today or tomorrow. I will edit this post to include a Python script for uploading to the database once I have it ready.

Edit (August 1st, 2026, 3:13 PM): I probably won't be able to finish the database code today, but I'll keep trying.

Uploading script:

Code: Select all

from random import randint

from subprocess import run

rle = input("Spaceship RLE: ")

rule = input("Rule: ")

dx = int(input("Spaceship dx: "))

dy = int(input("Spaceship dy: "))

period = int(input("Spaceship period: "))

r = randint(0, 100000000000000)

path = "/Users/YOUR USERNAME HERE/tmp"

with open(f"{path}/{r}.entry", "w") as file:
	
	file.write(f"{dx} : {dy} : {period} : {rule} : {rle},\n")
	
run(f"/usr/local/bin/curl -T {path}/{r}.entry https://sim.zpsou.eu/interna242/~mamaral/gliders/uncommitted/{r}.entry", shell = True)

run(f"cd {path} && rm -rf *.entry", shell = True)
Some notes:

- Please don't submit too many spaceships until I finish the database code.

- Spaceships run between southward and southeastward.

- There is currently no attribution system. I'll prepare one as soon as I finish the main database updater.

- B0 rules are currently not supported.
Last edited by MDA on August 14th, 2026, 8:29 am, edited 1 time in total.
My website (a database of Life objects, WIP).
GlidINT has been released!
Currently working on a forum system for my website.

Code: Select all

x = 5, y = 17, rule = B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-akHistory
C$2C$.2C$2C11$.3D$3.D$2.3D!
[[ STOP 72 ]]
User avatar
MDA
Posts: 294
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

Re: INT version of gliderdb

Post by MDA »

Edit (August 10th, 2026, 1:57 PM): GlidINT is now open. It doesn't support B0 rules yet, and all patterns have to be oriented between south and southeast to be accepted.

I took the liberty of uploading the glider, LWSS, MWSS, Glider 3736, and the bomber to the database. It can be accessed at https://sim.zpsou.eu/interna242/~mamara ... lidINT.txt.

Python uploader (requires Curl to work):

Code: Select all

from random import randint

from subprocess import run

rle = input("Spaceship RLE: ")

rule = input("Rule: ")

dx = int(input("Spaceship dx: "))

dy = int(input("Spaceship dy: "))

if dx <= 0 or dy < 0:
	
	raise ValueError("dx must be positive and dy must be nonnegative")
	
if dy > dx:
	
	raise ValueError("dx must be greater than or equal to dy")
	
period = int(input("Spaceship period: "))

r = randint(0, 100000000000000)

path = "/Users/YOUR USERNAME HERE/tmp"

with open(f"{path}/{r}.entry", "w") as file:
	
	file.write(f"{dy} : {dx} : {period} : {rule} : {rle},\n")
	
run(f"curl -T {path}/{r}.entry https://sim.zpsou.eu/interna242/~mamaral/gliders/uncommitted/{r}.entry", shell = True)
To make this work, I rewrote the database updater in Java instead of C.

Old post:
MDA wrote: August 1st, 2026, 3:09 pm I'm sorry for tripleposting, but I just wanted to increase this request's visibility.

I have finished writing the database updater. Unfortunately, the code's not working right now. The updater was never meant to be public, but I felt that it was important for me to post it here.

Could anyone try fixing the bugs in this program so I can use it for the database?

Code:

Code: Select all

#include <stdlib.h>

#include <stdio.h>

#include <ctype.h>

#include "dict.h"

#define RLE 160401

typedef struct Speed {
	
	long long dx;
	
	long long dy;
	
	long long period;
	
} Speed;

typedef struct Rulespace {
	
	char minrule[274];
	
	char rule[274];
	
	char maxrule[274];
	
} Rulespace;

typedef struct Conds {
	
	int bconds[117];
	
	int sconds[117];
	
} Conds;

typedef struct Glider {
	
	long long dx;
	
	long long dy;
	
	long long period;
	
	int minpop;
	
	char minrule[274];
	
	char maxrule[274];
	
} Glider;

void open(char filepath[], Speed * speed, Rulespace * rulespace, char (* rle)[RLE]) {
	
	int n = 0;
	
	char c;
	
	(*speed).dx = 0;
	
	(*speed).dy = 0;
	
	(*speed).period = 0;
	
	FILE * file;
	
	file = fopen(filepath, "r");
	
	c = getc(file);
	
	while (isdigit(c)) {
		
		(*speed).dx *= 10;
		
		(*speed).dx += (c - '0');
		
		c = getc(file);
		
	}
	
	/* SPACE */
	
	c = getc(file);
	
	/* COLON */
	
	c = getc(file);
	
	/* SPACE */
	
	c = getc(file);
	
	while (isdigit(c)) {
		
		(*speed).dy *= 10;
		
		(*speed).dy += (c - '0');
		
		c = getc(file);
		
	}
	
	/* SPACE */
	
	c = getc(file);
	
	/* COLON */
	
	c = getc(file);
	
	/* SPACE */
	
	c = getc(file);
	
	while (isdigit(c)) {
		
		(*speed).period *= 10;
		
		(*speed).period += (c - '0');
		
		c = getc(file);
		
	}
	
	/* SPACE */
	
	c = getc(file);
	
	/* COLON */
	
	c = getc(file);
	
	/* SPACE */
	
	c = getc(file);
	
	while (c != ' ') {
		
		(*rulespace).rule[n] = c;
		
		n++;
		
		c = getc(file);
		
	}
	
	(*rulespace).rule[n] = 0;
	
	n = 0;
	
	/* SPACE */
	
	c = getc(file);
	
	/* COLON */
	
	c = getc(file);
	
	/* SPACE */
	
	c = getc(file);
	
	while (c != ',') {
		
		(*rle)[n] = c;
		
		n++;
		
		c = getc(file);
		
	}
	
	(*rle)[n] = 0;
	
	fclose(file);
	
}

void rparse(char rule[274], Conds * conds) {
	
	int n = 1; /* Skip the leading B */
	
	int negating = 0;
	
	int lastdigit = -1;
	
	char letters[14] = "cekainyqjrtwz";
	
	for (int nn = 0; nn < 117; nn++) {
		
		(*conds).bconds[nn] = 0;
		
		(*conds).sconds[nn] = 0;
		
	}
	
	while (rule[n] != '/') {
		
		if (isdigit(rule[n])) {
			
			for (int nn = 0; nn < 13; nn++) {
				
				(*conds).bconds[((rule[n] - '0') * 13) + nn] = 2;
				
				negating = 0;
				
				lastdigit = rule[n] - '0';
				
			}
			
		}
		
		if (isalpha(rule[n])) {
			
			if (negating) {
				
				for (int nn = 0; nn < 13; nn++) {
					
					if (rule[n] == letters[nn]) {
						
						(*conds).bconds[((rule[n] - '0') * 13) + nn] = 0;
						
					}
					
				}
				
			} else {
				
				for (int nn = 0; nn < 13; nn++) {
					
					if ((*conds).bconds[((rule[n] - '0') * 13) + nn] == 2) (*conds).bconds[((rule[n] - '0') * 13) + nn] = 0;
					
					if (rule[n] == letters[nn]) (*conds).bconds[((rule[n] - '0') * 13) + nn] = 1;
					
				}
				
			}
			
		}
		
		if (rule[n] == '-') negating = 1;
		
		n++;
		
	}
	
	n += 2; /* Skip "/S" */
	
	negating = 0;
	
	lastdigit = -1;
	
	while (rule[n]) {
		
		if (isdigit(rule[n])) {
			
			for (int nn = 0; nn < 13; nn++) {
				
				(*conds).sconds[((rule[n] - '0') * 13) + nn] = 2;
				
				negating = 0;
				
				lastdigit = rule[n] - '0';
				
			}
			
		}
		
		if (isalpha(rule[n])) {
			
			if (negating) {
				
				for (int nn = 0; nn < 13; nn++) {
					
					if (rule[n] == letters[nn]) {
						
						(*conds).sconds[((rule[n] - '0') * 13) + nn] = 0;
						
					}
					
				}
				
			} else {
				
				for (int nn = 0; nn < 13; nn++) {
					
					if ((*conds).sconds[((rule[n] - '0') * 13) + nn] == 2) (*conds).sconds[((rule[n] - '0') * 13) + nn] = 0;
					
					if (rule[n] == letters[nn]) (*conds).sconds[((rule[n] - '0') * 13) + nn] = 1;
					
				}
				
			}
			
		}
		
		if (rule[n] == '-') negating = 1;
		
		n++;
		
	}
	
}

void makebb(char rle[RLE], int * bx, int * by) {
	
	int bbx = 0, bbxr = 0, bby = 0;
	
	int n = 0;
	
	int run;
	
	while (rle[n] != '!') {
		
		if (isdigit(rle[n])) {
			
			run *= 10;
			
			run += (rle[n] - '0');
			
		}
		
		if ((rle[n] == 'b') || (rle[n] == 'o')) {
			
			int r = (run == 0) ? 1 : run;
			
			for (int nn = 0; nn < r; nn++) {
				
				if (bbx > bbxr) {
					
					bbx++;
					
					bbxr++;
					
				} else {
					
					bbx++;
					
				}
				
			}
			
		}
		
		if (rle[n] == '$') {
			
			bbx = 0;
			
			bby++;
			
		}
		
		n++;
		
	}
	
	*bx = bbxr;
	
	*by = bby;
	
}

void makeworld(char rle[RLE], int bbx, int bby, int (* world)[bby][bbx], Speed speed) {
	
	int x = speed.period, y = speed.period;
	
	int n = 0;
	
	int run;
	
	while (rle[n] != '!') {
		
		if (isdigit(rle[n])) {
			
			run *= 10;
			
			run += (rle[n] - '0');
			
		}
		
		if (rle[n] == 'b') {
			
			int r = (run == 0) ? 1 : run;
			
			for (int nn = 0; nn < r; nn++) {
				
				(*world)[y][x] = 0;
				
				x++;
				
			}
			
		}
		
		if (rle[n] == 'o') {
			
			int r = (run == 0) ? 1 : run;
			
			for (int nn = 0; nn < r; nn++) {
				
				(*world)[y][x] = 1;
				
				x++;
				
			}
			
		}
		
		if (rle[n] == '$') {
			
			y++;
			
			x = speed.period;
			
		}
		
		n++;
		
	}
	
}

void advance(int bbx, int bby, int (* world)[bby][bbx], Conds conds) {
	
	int binary;
	
	int neigh[8][2] = {{1, 1}, {1, 0}, {1, -1}, {0, 1}, {0, -1}, {-1, 1}, {-1, 0}, {-1, -1}};
	
	int next[bby][bbx];
	
	for (int y = 0; y < bby; y++) for (int x = 0; x < bbx; x++) next[y][x] = 0;
	
	for (int y = 0; y < bby; y++) {
		
		for (int x = 0; x < bbx; x++) {
			
			binary = 0;
			
			for (int n = 0; n < 8; n++) {
				
				binary *= 2;
				
				if ((y + neigh[n][0] < 0) || (y + neigh[n][0] >= bby) || (x + neigh[n][1] < 0) || (x + neigh[n][1] >= bbx)) continue;
				
				binary += (*world)[y][x];
				
			}
			
			if (!(*world)[y][x] && (conds.bconds[dict[binary]])) next[y][x] = 1;
			
			if ((*world)[y][x] && (conds.sconds[dict[binary]])) next[y][x] = 1;
			
		}
		
	}
	
	for (int y = 0; y < bby; y++) for (int x = 0; x < bbx; x++) (*world)[y][x] = next[y][x];
	
}

int getpop(int bbx, int bby, int world[bby][bbx]) {
	
	int pop = 0;
	
	for (int y = 0; y < bby; y++) for (int x = 0; x < bbx; x++) if (world[y][x]) pop++;
	
	return pop;
	
}

int verify(int bbx, int bby, int world[bby][bbx], Speed speed, Conds conds) {
	
	int equal = 1;
	
	int orig[bby][bbx];
	
	int iter[bby][bbx];
	
	int comp[bby][bbx];
	
	for (int y = 0; y < bby; y++) for (int x = 0; x < bbx; x++) {
		
		orig[y][x] = world[y][x];
		
		iter[y][x] = world[y][x];
		
		comp[y][x] = 0;
		
	}
	
	for (int n = 0; n < speed.period; n++) advance(bbx, bby, &iter, conds);
	
	for (int y = speed.dy; y < bby; y++) for (int x = speed.dx; x < bbx; x++) comp[y - speed.dy][x - speed.dx] = iter[y][x];
	
	for (int y = 0; y < bby; y++) for (int x = 0; x < bbx; x++) if (comp[y][x] != orig[y][x]) equal = 0;
	
	return equal;
	
}

void sort(char (* letters)[14]) { /* Basic selection sort */
	
	int n = 0;
	
	int current;
	
	char sorted[14] = "";
	
	for (int current = 'a'; current <= 'z'; current++) {
		
		for (int nn = 0; nn < 13; nn++) {
			
			if ((*letters)[nn] == current) {
				
				sorted[n] = current;
				
				n++;
				
			}
			
		}
		
	}
	
	for (int n = 0; n < 13; n++) (*letters)[n] = sorted[n];
	
}

void writerule(char (* rulestring)[274], Conds conds) { /* Does the opposite of rparse */
	
	int negating;
	
	int positive;
	
	int n = 0;
	
	int vals[9] = {1, 2, 6, 10, 13, 10, 6, 2, 1};
	
	char letters[14] = "cekainyqjrtwz";
	
	char used[14] = "";
	
	(*rulestring)[n] = 'B';
	
	n++;
	
	for (int c = 0; c < 9; c++) {
		
		positive = 0;
		
		for (int m = 0; m < vals[c]; m++) if (conds.bconds[(c * 13) + m]) positive++;
		
		if (!positive) continue;
		
		(*rulestring)[n] = c + '0';
		
		n++;
		
		if ((vals[c] - positive) < (vals[c] / 2)) negating = 1;
		
		int nn = 0;
		
		if (negating) {
			
			(*rulestring)[n] = '-';
			
			n++;
			
			for (int m = 0; m < vals[c]; m++) {
				
				if (!(conds.bconds[(c * 13) + m])) {
					
					used[nn] = letters[m];
					
					nn++;
					
				}
				
			}
			
			sort(&used);
			
			nn = 0;
			
			while (used[nn]) {
				
				(*rulestring)[n] = used[nn];
				
				nn++;
				
				n++;
				
			}
			
		} else {
			
			for (int m = 0; m < vals[c]; m++) {
				
				if ((conds.bconds[(c * 13) + m])) {
					
					used[nn] = letters[m];
					
					nn++;
					
				}
				
			}
			
			sort(&used);
			
			nn = 0;
			
			while (used[nn]) {
				
				(*rulestring)[n] = used[nn];
				
				nn++;
				
				n++;
				
			}
			
		}
		
	}
	
	(*rulestring)[n] = '/';
	
	n++;
	
	(*rulestring)[n] = 'S';
	
	n++;
	
	for (int c = 0; c < 9; c++) {
		
		positive = 0;
		
		for (int m = 0; m < vals[c]; m++) if (conds.sconds[(c * 13) + m]) positive++;
		
		if (!positive) continue;
		
		(*rulestring)[n] = c + '0';
		
		n++;
		
		if ((vals[c] - positive) < (vals[c] / 2)) negating = 1;
		
		int nn = 0;
		
		if (negating) {
			
			(*rulestring)[n] = '-';
			
			n++;
			
			for (int m = 0; m < vals[c]; m++) {
				
				if (!(conds.sconds[(c * 13) + m])) {
					
					used[nn] = letters[m];
					
					nn++;
					
				}
				
			}
			
			sort(&used);
			
			nn = 0;
			
			while (used[nn]) {
				
				(*rulestring)[n] = used[nn];
				
				nn++;
				
				n++;
				
			}
			
		} else {
			
			for (int m = 0; m < vals[c]; m++) {
				
				if ((conds.sconds[(c * 13) + m])) {
					
					used[nn] = letters[m];
					
					nn++;
					
				}
				
			}
			
			sort(&used);
			
			nn = 0;
			
			while (used[nn]) {
				
				(*rulestring)[n] = used[nn];
				
				nn++;
				
				n++;
				
			}
			
		}
		
	}
	
}

void getmaxrule(int bbx, int bby, int world[bby][bbx], Speed speed, Conds (* conds)) {
	
	for (int nn = 0; nn < 117; nn++) {
		
		(*conds).bconds[nn] = 0;
		
		(*conds).sconds[nn] = 0;
		
	}
	
	for (int n = 0; n < 117; n++) {
		
		(*conds).bconds[n] = 1;
		
		if (!(verify(bbx, bby, world, speed, *conds))) (*conds).bconds[n] = 0;
		
	}
	
	for (int n = 0; n < 117; n++) {
		
		(*conds).sconds[n] = 1;
		
		if (!(verify(bbx, bby, world, speed, *conds))) (*conds).sconds[n] = 0;
		
	}
	
}

void getminrule(int bbx, int bby, int world[bby][bbx], Speed speed, Conds (* conds)) {
	
	for (int nn = 0; nn < 117; nn++) {
		
		(*conds).bconds[nn] = 0;
		
		(*conds).sconds[nn] = 0;
		
	}
	
	for (int n = 0; n < 117; n++) {
		
		(*conds).bconds[n] = 0;
		
		if (!(verify(bbx, bby, world, speed, *conds))) (*conds).bconds[n] = 1;
		
	}
	
	for (int n = 0; n < 117; n++) {
		
		(*conds).sconds[n] = 0;
		
		if (!(verify(bbx, bby, world, speed, *conds))) (*conds).sconds[n] = 1;
		
	}
	
}

int getminpop(int bbx, int bby, int world[bby][bbx], int (* target)[bby][bbx], Speed speed, Conds conds) {
	
	int pop = getpop(bbx, bby, world);
	
	int min[bby][bbx];
	
	int iter[bby][bbx];
	
	for (int y = 0; y < bby; y++) for (int x = 0; x < bbx; x++) {
		
		min[y][x] = world[y][x];
		
		iter[y][x] = world[y][x];
		
	}
	
	for (int n = 0; n < speed.period; n++) {
		
		if (getpop(bbx, bby, iter) < pop) {
			
			pop = getpop(bbx, bby, iter);
			
			for (int y = 0; y < bby; y++) for (int x = 0; x < bbx; x++) (*target)[y][x] = iter[y][x];
			
		}
		
		advance(bbx, bby, &iter, conds);
		
	}
	
	return pop;
	
}

int sgmp(int bbx, int bby, int world[bby][bbx], Speed speed, Conds conds) { /* Silently get minpop */
	
	int pop = getpop(bbx, bby, world);
	
	int iter[bby][bbx];
	
	for (int y = 0; y < bby; y++) for (int x = 0; x < bbx; x++) {
		
		iter[y][x] = world[y][x];
		
	}
	
	for (int n = 0; n < speed.period; n++) {
		
		if (getpop(bbx, bby, iter) < pop) {
			
			pop = getpop(bbx, bby, iter);
			
		}
		
		advance(bbx, bby, &iter, conds);
		
	}
	
	return pop;
	
}

int power(int a, int b) {
	
	int x = 1;
	
	for (int n = 0; n < b; n++) x *= a;
	
	return x;
	
}

int calcpow(int n) {
	
	int nn;
	
	for (nn = 0; n < power(10, nn); nn++);
	
	return nn;
	
}

void makerle(int bbx, int bby, int world[bby][bbx], char (* rle)[RLE]) {
	
	int n = 0;
	
	int run = 1;
	
	int lastdigit = -1;
	
	for (int nn = 0; nn < RLE; nn++) (*rle)[nn] = 0;
	
	for (int y = 0; y < bby; y++) {
		
		for (int x = 0; x < bbx; x++) {
			
			if (world[y][x] == lastdigit) {
				
				run++;
				
			} else {
				
				if (run == 1) {
					
					if (world[y][x]) {
						
						(*rle)[n] = 'o';
						
					} else {
						
						(*rle)[n] = 'b';
						
					}
					
					n++;
					
				} else {
					
					for (int nn = calcpow(run); nn > 0; nn--) {
						
						(*rle)[n] = '0' + ((run % power(10, nn)) / power(10, (nn - 1)));
						
						n++;
						
					}
					
					if (world[y][x]) {
						
						(*rle)[n] = 'o';
						
					} else {
						
						(*rle)[n] = 'b';
						
					}
					
					n++;
					
				}
				
			}
			
		}
		
		(*rle)[n] = '$';
		
		n++;
		
	}
	
	(*rle)[n - 1] = '!';
	
}

int abovelower(Conds test, Conds lower) {
	
	int above = 1;
	
	for (int n = 0; n < 117; n++) {
		
		if (((test.bconds)[n] == 0) && ((lower.bconds)[n] == 1)) {
			
			above = 0;
			
			return above;
			
		}
		
	}
	
	for (int n = 0; n < 117; n++) {
		
		if (((test.sconds)[n] == 0) && ((lower.sconds)[n] == 1)) {
			
			above = 0;
			
			return above;
			
		}
		
	}
	
	return above;
	
}

int belowupper(Conds test, Conds upper) {
	
	int below = 1;
	
	for (int n = 0; n < 117; n++) {
		
		if (((test.bconds)[n] == 1) && ((upper.bconds)[n] == 0)) {
			
			below = 0;
			
			return below;
			
		}
		
	}
	
	for (int n = 0; n < 117; n++) {
		
		if (((test.sconds)[n] == 1) && ((upper.sconds)[n] == 0)) {
			
			below = 0;
			
			return below;
			
		}
		
	}
	
	return below;
	
}

int getglidid(int processed) { /* Returns an ID from a line number */
	
	int commacount = 0;
	
	int id = 1; /* Raw ID */
	
	FILE * gliders;
	
	gliders = fopen("/Users/mamaral/Sites/gliders/GlidINT.json", "r");
	
	while (commacount != (processed - 1)) {
		
		if (getc(gliders) == ',') commacount++;
		
	}
	
	char c;
	
	while ((c = getc(gliders)) != '!'); /* Scans until the end of the RLE */
	
	getc(gliders); /* Gets ' ' */
	
	getc(gliders); /* Gets ':' */
	
	getc(gliders); /* Gets ' ' */
	
	while ((c = getc(gliders)) != ',') {
		
		id *= 10;
		
		id += (c - '0');
		
	}
	
	return id;
	
}

int processid(int id, int maxid) { /* Looks for a specific ID */
	
	int n = 1;
	
	FILE * gliders;
	
	gliders = fopen("/Users/mamaral/Sites/gliders/GlidINT.json", "r");
	
	for (int n = 1; ; n++) {
		
		if (getglidid(n) == id) return n;
		
		if (n > maxid) break;
		
	}
	
	return -1;
	
}

void getdata(int processed, Glider * glider) {
	
	int commacount = 0;
	
	FILE * gliders;
	
	gliders = fopen("/Users/mamaral/Sites/gliders/GlidINT.json", "r");
	
	while (commacount != (processed - 1)) {
		
		if (getc(gliders) == ',') commacount++;
		
	}
	
	getc(gliders); /* Gets '\n' */
	
	char c;
	
	(*glider).dx = 0;
	
	while ((c = getc(gliders)) != ' ') {
		
		(*glider).dx *= 10;
		
		(*glider).dx += (c - '0');
		
	}
	
	getc(gliders); /* Gets ':' */
	
	getc(gliders); /* Gets ' ' */
	
	(*glider).dy = 0;
	
	while ((c = getc(gliders)) != ' ') {
		
		(*glider).dy *= 10;
		
		(*glider).dy += (c - '0');
		
	}
	
	getc(gliders); /* Gets ':' */
	
	getc(gliders); /* Gets ' ' */
	
	(*glider).period = 0;
	
	while ((c = getc(gliders)) != ' ') {
		
		(*glider).period *= 10;
		
		(*glider).period += (c - '0');
		
	}
	
	getc(gliders); /* Gets ':' */
	
	getc(gliders); /* Gets ' ' */
	
	(*glider).minpop = 0;
	
	while ((c = getc(gliders)) != ' ') {
		
		(*glider).minpop *= 10;
		
		(*glider).minpop += (c - '0');
		
	}
	
	getc(gliders); /* Gets ':' */
	
	getc(gliders); /* Gets ' ' */
	
	for (int n = 0; n < 274; n++) (*glider).minrule[n] = 0;
	
	int nn = 0;
	
	while ((c = getc(gliders)) != ' ') {
		
		(*glider).minrule[nn] = c;
		
		nn++;
		
	}
	
	getc(gliders); /* Gets ':' */
	
	getc(gliders); /* Gets ' ' */
	
	for (int n = 0; n < 274; n++) (*glider).maxrule[n] = 0;
	
	nn = 0;
	
	while ((c = getc(gliders)) != ' ') {
		
		(*glider).maxrule[nn] = c;
		
		nn++;
		
	}
	
	fclose(gliders);
	
}

int getid(void) {
	
	int id = 0;
	
	FILE * num;
	
	num = fopen("/Users/mamaral/Sites/gliders/ngliders.txt", "r");
	
	char c;
	
	while ((c = getc(num)) != EOF) {
		
		id *= 10;
		
		id += (c - '0');
		
	}
	
	fclose(num);
	
	return id;
	
}

void putid(void) {
	
	int id = getid();
	
	id++;
	
	FILE * num;
	
	num = fopen("/Users/mamaral/Sites/gliders/ngliders.txt", "w");
	
	for (int n = calcpow(id); n > 0; n--) {
		
		putc(('0' + ((id % power(10, n)) / power(10, n - 1))), num);
		
	}
	
	fclose(num);
	
}

void transpose(int processed, Speed speed, int (* world)[400 + (speed.period * 2)][400 + (speed.period * 2)]) { /* Get a glider RLE and put it in a world */
	
	int commacount = 0;
	
	int bbx = 400 + (speed.period * 2);
	
	int bby = bbx;
	
	char rle[RLE] = "";
	
	FILE * gliders;
	
	gliders = fopen("/Users/mamaral/Sites/gliders/GlidINT.json", "r");
	
	while (commacount != (processed - 1)) {
		
		if (getc(gliders) == ',') commacount++;
		
	}
	
	getc(gliders); /* Gets '\n' */
	
	char c;
	
	while ((c = getc(gliders)) != ' ');
	
	getc(gliders); /* Gets ':' */
	
	getc(gliders); /* Gets ' ' */
	
	while ((c = getc(gliders)) != ' ');
	
	getc(gliders); /* Gets ':' */
	
	getc(gliders); /* Gets ' ' */
	
	while ((c = getc(gliders)) != ' ');
	
	getc(gliders); /* Gets ':' */
	
	getc(gliders); /* Gets ' ' */
	
	while ((c = getc(gliders)) != ' ');
	
	getc(gliders); /* Gets ':' */
	
	getc(gliders); /* Gets ' ' */
	
	while ((c = getc(gliders)) != ' ');
	
	getc(gliders); /* Gets ':' */
	
	getc(gliders); /* Gets ' ' */
	
	while ((c = getc(gliders)) != ' ');
	
	getc(gliders); /* Gets ':' */
	
	getc(gliders); /* Gets ' ' */
	
	int nn;
	
	while ((c = getc(gliders)) != ' ') {
		
		rle[nn] = c;
		
	}
	
	makeworld(rle, bbx, bby, world, speed);
	
}

void getredundantgliders(int (* list)[65536] /* Assume a maximum of 65,536 gliders made redundant */, int maxid, int bbex, int bbey, int exworld[bbey][bbex], Speed speed, Rulespace rulespace) { /* Gets the IDs of all redundant gliders */
	
	int n = 0;
	
	int processed;
	
	int bbx = 400 + (2 * speed.period);
	
	int bby = bbx;
	
	int world[bby][bbx];
	
	int oldmp = 0;
	
	int newmp = 0;
	
	for (int y = 0; y < bby; y++) for (int x = 0; x < bbx; x++) world[y][x] = 0;
	
	char minrule[274] = "";
	
	char maxrule[274] = "";
	
	Glider current;
	
	Conds conds;
	
	Conds oldlower;
	
	Conds oldupper;
	
	Conds newlower;
	
	Conds newupper;
	
	rparse(rulespace.rule, &conds);
	
	for (int nn = 0; nn < 65536; nn++) (*list)[nn] = 0;
	
	for (int nn = 1; nn <= maxid; nn++) {
		
		processed = processid(nn, maxid);
		
		if (processed == -1) continue; /* Skips deleted ships */
		
		getdata(processed, &current);
		
		if ((current.dx != speed.dx) || (current.dy != speed.dy) || (current.period != speed.period)) continue;
		
		transpose(processed, speed, &world);
		
		oldmp = sgmp(bbx, bby, world, speed, conds);
		
		newmp = sgmp(bbex, bbey, exworld, speed, conds);
		
		getminrule(bbex, bbey, world, speed, &newlower);
		
		getmaxrule(bbex, bbey, world, speed, &newlower);
		
		getminrule(bbx, bby, world, speed, &oldlower);
		
		getmaxrule(bbx, bby, world, speed, &oldupper);
		
		if (newmp > oldmp) continue; /* Some gliders may be made redundant but this one won't since it's smaller */
		
		if (abovelower(oldlower, newlower) && belowupper(oldupper, newupper)) { /* The new glider supports every rule the old glider does */
			
			(*list)[n] = nn; /* Found a redundant glider; add to list */
			
		}
		
	}
	
}

int isnotable(int maxid, int bbex, int bbey, int exworld[bbey][bbex], Speed speed, Rulespace rulespace) { /* Preliminary check */
	
	/* Logical values */
	
	// int docrule = 0;
	
	int docspeed = 0;
	
	int versatile = 0;
	
	int optimal = 0;
	
	/* ---------------- */
	
	int processed;
	
	int bbx = 400 + (2 * speed.period);
	
	int bby = bbx;
	
	int oldmp = 0;
	
	int newmp = 0;
	
	int world[bby][bbx];
	
	Glider current;
	
	Conds conds;
	
	Conds oldlower;
	
	Conds oldupper;
	
	Conds medrule;
	
	Conds newlower;
	
	Conds newupper;
	
	rparse(rulespace.rule, &medrule);
	
	for (int nn = 1; nn <= maxid; nn++) {
		
		processed = processid(nn, maxid);
		
		if (processed == -1) continue; /* Skips deleted ships */
		
		transpose(processed, speed, &world);
		
		getminrule(bbex, bbey, exworld, speed, &newlower);
		
		getmaxrule(bbex, bbey, exworld, speed, &newlower);
		
		getminrule(bbx, bby, world, speed, &oldlower);
		
		getmaxrule(bbx, bby, world, speed, &oldupper);
		
		if (!(abovelower(medrule, oldlower) && belowupper(medrule, oldupper))) continue; /* Skips spaceships alien to the selected rule */
		
		if (abovelower(newlower, oldlower) && belowupper(newupper, oldupper)) { /* The new spaceship works in a strict subset of the old one's rulespace */
			
			getdata(processed, &current);
			
			if ((current.dx == speed.dx) && (current.dy == speed.dy) && (current.period == speed.period)) {
				
				docspeed = 1;
				
				oldmp = sgmp(bbx, bby, world, speed, conds);
				
				newmp = sgmp(bbex, bbey, exworld, speed, conds);
				
				if (newmp < oldmp) {
					
					return 1;
					
				} else {
					
					return 0;
					
				}
				
			}
			
		} else { /* The new spaceship works in a different ruleset */
			
			versatile = 1;
			
			return 1;
			
		}
		
	}
	
	return 1;
	
}

int in(int id, int list[65536]) {
	
	for (int n = 0; n < 65536; n++) {
		
		if (!(list[n])) break;
		
		if (list[n] == id) return 1;
		
	}
	
	return 0;
	
}

void move(int (* list)[65536], int maxid) {
	
	int n = 0;
	
	while ((*list)[n]) {
		
		(*list)[n] = processid((*list)[n], maxid);
		
	}
	
	FILE * original;
	
	FILE * buffer;
	
	original = fopen("/Users/mamaral/Sites/gliders/GlidINT.json", "r");
	
	buffer = fopen("/Users/mamaral/Sites/gliders/buffer.json", "w");
	
	int commacount = 0;
	
	char c;
	
	while ((c = getc(original))) {
		
		if (!(in((commacount + 1), *list))) {
			
			putc(c, buffer);
			
		}
		
	}
	
	fclose(original);
	
	fclose(buffer);
	
}

void compose(char (* entry)[200000], int minpop, int id, Speed speed, Rulespace rulespace, char rle[RLE]) {
	
	for (int n = 0; n < 200000; n++) (*entry)[n] = 0;
	
	int n = 0;
	
	for (int nn = calcpow(speed.dx); nn > 0; nn--) {
		
		(*entry)[n] = '0' + ((speed.dx % power(10, nn)) / power(10, (nn - 1)));
		
		n++;
		
	}
	
	(*entry)[n] = ' '; n++;
	
	(*entry)[n] = ':'; n++;
	
	(*entry)[n] = ' '; n++;
	
	for (int nn = calcpow(speed.dy); nn > 0; nn--) {
		
		(*entry)[n] = '0' + ((speed.dy % power(10, nn)) / power(10, (nn - 1)));
		
		n++;
		
	}
	
	(*entry)[n] = ' '; n++;
	
	(*entry)[n] = ':'; n++;
	
	(*entry)[n] = ' '; n++;
	
	for (int nn = calcpow(speed.period); nn > 0; nn--) {
		
		(*entry)[n] = '0' + ((speed.period % power(10, nn)) / power(10, (nn - 1)));
		
		n++;
		
	}
	
	(*entry)[n] = ' '; n++;
	
	(*entry)[n] = ':'; n++;
	
	(*entry)[n] = ' '; n++;
	
	for (int nn = calcpow(minpop); nn > 0; nn--) {
		
		(*entry)[n] = '0' + ((minpop % power(10, nn)) / power(10, (nn - 1)));
		
		n++;
		
	}
	
	(*entry)[n] = ' '; n++;
	
	(*entry)[n] = ':'; n++;
	
	(*entry)[n] = ' '; n++;
	
	int nn = 0;
	
	while (rulespace.minrule[nn]) {
		
		(*entry)[n] = rulespace.minrule[nn];
		
		nn++;
		
		n++;
		
	}
	
	(*entry)[n] = ' '; n++;
	
	(*entry)[n] = ':'; n++;
	
	(*entry)[n] = ' '; n++;
	
	nn = 0;
	
	while (rulespace.maxrule[nn]) {
		
		(*entry)[n] = rulespace.maxrule[nn];
		
		nn++;
		
		n++;
		
	}
	
	(*entry)[n] = ' '; n++;
	
	(*entry)[n] = ':'; n++;
	
	(*entry)[n] = ' '; n++;
	
	nn = 0;
	
	while (rle[nn]) {
		
		(*entry)[n] = rle[nn];
		
		nn++;
		
		n++;
		
	}
	
	(*entry)[n] = ' '; n++;
	
	(*entry)[n] = ':'; n++;
	
	(*entry)[n] = ' '; n++;
	
	for (int nn = calcpow(id); nn > 0; nn--) {
		
		(*entry)[n] = '0' + ((id % power(10, nn)) / power(10, (nn - 1)));
		
		n++;
		
	}
	
	(*entry)[n] = ','; n++;
	
	(*entry)[n] = '\n';
	
}

void write(char entry[200000]) {
	
	int n = 0;
	
	FILE * gliders;
	
	gliders = fopen("/Users/mamaral/Sites/gliders/GlidINT.json", "a");
	
	while (entry[n]) {
		
		putc(entry[n], gliders);
		
		n++;
		
	}
	
	fclose(gliders);
	
}

int main(int argc, char * argv[]) {
	
	int bx = 0, by = 0;
	
	int bbx, bby;
	
	int maxid = getid();
	
	int minpop;
	
	int list[65536] = {0};
	
	char rle[RLE] = {0};
	
	char entry[200000] = "";
	
	if (argc == 1) {
		
		printf("Usage: commit (filepath)\n");
		
		return 0;
		
	}
	
	Speed speed;
	
	Rulespace rulespace;
	
	Conds conds;
	
	Conds minc;
	
	Conds maxc;
	
	for (int cn = 0; cn < 117; cn++) {
		
		minc.bconds[cn] = conds.bconds[cn];
		
		minc.sconds[cn] = conds.sconds[cn];
		
		maxc.bconds[cn] = conds.bconds[cn];
		
		maxc.sconds[cn] = conds.sconds[cn];
		
	}
	
	open(argv[1], &speed, &rulespace, &rle);
	
	makebb(rle, &bx, &by);
	
	bbx = bx + (2 * speed.period);
	
	bby = by + (2 * speed.period);
	
	int world[bby][bbx];
	
	for (int y = 0; y < bby; y++) for (int x = 0; x < bbx; x++) world[y][x] = 0;
	
	makeworld(rle, bbx, bby, &world, speed);
	
	if (!verify(bbx, bby, world, speed, conds)) return 0; /* Removes faulty spaceships */
	
	if (!getpop(bbx, bby, world)) return 0; /* Filters zero-population ships */
	
	if (!isnotable(maxid, bbx, bby, world, speed, rulespace)) return 0; /* Filters duplicates or completely suboptimal ships */
	
	getredundantgliders(&list, maxid, bbx, bby, world, speed, rulespace);
	
	move(&list, maxid);
	
	system("/Users/mamaral/Sites/gliders/ mv buffer.json GlidINT.json");
	
	getminrule(bbx, bby, world, speed, &minc);
	
	getmaxrule(bbx, bby, world, speed, &maxc);
	
	writerule(&(rulespace.minrule), minc);
	
	writerule(&(rulespace.maxrule), maxc);
	
	minpop = getminpop(bbx, bby, world, &world, speed, conds); /* Assigns the ship's minpop to "minpop" and shifts the ship to the minpop phase */
	
	makerle(bbx, bby, world, &rle);
	
	maxid++;
	
	compose(&entry, minpop, maxid, speed, rulespace, rle);
	
	write(entry);
	
	/* Format: dx : dy : period : minpop : minrule : maxrule : rle : entrynum, */
	
}
It requires a header file called "dict.h", available here.

Fixes of any size are welcome.
Edit (August 11th, 2026, 7:15 PM): I wrote a searcher for the database in Java:

Code: Select all

import java.io.*;

// import java.util.Scanner;

class Parser {
	
	public String rulestring;
	
	Parser() {
		
		this.rulestring = "";
		
	}
	
	void parse(String arg) {
		
		this.rulestring = arg;
		
	}
	
}

class Funcs {
	
	private String rulestring;
	
	public int[] bconds = new int[117], sconds = new int[117];
	
	private int[] getminrule_bconds = new int[117], getminrule_sconds = new int[117], getmaxrule_bconds = new int[117], getmaxrule_sconds = new int[117];
	
	String gliders = "/path/to/GlidINT/directory";
	
	String idpath = "/path/to/ngliders/directory";
	
	FileInputStream nextg;
	
	Funcs(String rulestring) throws FileNotFoundException {
		
		this.rulestring = rulestring;
		
		nextg = new FileInputStream(gliders + "GlidINT.txt");
		
	}
	
	void rparse() {
		
		int lastdigit = -1;
		
		char c;
		
		boolean birth = false, survival = false;
		
		boolean negating = false;
		
		String configs = "cekainyqjrtwz";
		
		for (int n = 0; n < this.rulestring.length(); n++) {
			
			c = this.rulestring.charAt(n);
			
			if (c == '/') continue;
			
			if (Character.isDigit(c)) {
				
				lastdigit = c - '0';
				
				negating = false;
				
				for (int nn = 0; nn < 13; nn++) {
					
					if (birth) {
						
						this.bconds[(lastdigit * 13) + nn] = 2;
						
					}
					
					if (survival) {
						
						this.sconds[(lastdigit * 13) + nn] = 2;
						
					}
					
				}
				
			}
			
			if (c == 'B') {
				
				lastdigit = -1;
				
				birth = true;
				
				survival = false;
				
				negating = false;
				
			} else if (c == 'S') {
				
				lastdigit = -1;
				
				birth = false;
				
				survival = true;
				
				negating = false;
				
			} else if (Character.isLetter(c)) {
				
				if (birth) {
					
					if (negating) {
						
						this.bconds[(lastdigit * 13) + configs.indexOf(c)] = 0;
						
					} else {
						
						for (int nn = 0; nn < 13; nn++) {
							
							if (this.bconds[(lastdigit * 13) + nn] == 2) {
								
								this.bconds[(lastdigit * 13) + nn] = 0;
								
							}
							
							if (nn == configs.indexOf(c)) {
								
								this.bconds[(lastdigit * 13) + nn] = 1;
								
							}
							
						}
						
					}
					
				}
				
				if (survival) {
					
					if (negating) {
						
						this.sconds[(lastdigit * 13) + configs.indexOf(c)] = 0;
						
					} else {
						
						for (int nn = 0; nn < 13; nn++) {
							
							if (nn == configs.indexOf(c)) {
								
								this.sconds[(lastdigit * 13) + nn] = 1;
								
							} else if (this.sconds[(lastdigit * 13) + nn] == 2) {
								
								this.sconds[(lastdigit * 13) + nn] = 0;
								
							}
							
						}
						
					}
					
				}
				
			}
			
			if (c == '-') {
				
				negating = true;
				
			}
			
		}
		
	}
	
	int[][] rparse(String rulestring) {
		
		int lastdigit = -1;
		
		int[] bconds = new int[117];
		
		int[] sconds = new int[117];
		
		char c;
		
		boolean birth = false, survival = false;
		
		boolean negating = false;
		
		String configs = "cekainyqjrtwz";
		
		for (int n = 0; n < rulestring.length(); n++) {
			
			c = rulestring.charAt(n);
			
			if (c == '/') continue;
			
			if (Character.isDigit(c)) {
				
				lastdigit = c - '0';
				
				negating = false;
				
				for (int nn = 0; nn < 13; nn++) {
					
					if (birth) {
						
						bconds[(lastdigit * 13) + nn] = 2;
						
					}
					
					if (survival) {
						
						sconds[(lastdigit * 13) + nn] = 2;
						
					}
					
				}
				
			}
			
			if (c == 'B') {
				
				lastdigit = -1;
				
				birth = true;
				
				survival = false;
				
				negating = false;
				
			} else if (c == 'S') {
				
				lastdigit = -1;
				
				birth = false;
				
				survival = true;
				
				negating = false;
				
			} else if (Character.isLetter(c)) {
				
				if (birth) {
					
					if (negating) {
						
						bconds[(lastdigit * 13) + configs.indexOf(c)] = 0;
						
					} else {
						
						for (int nn = 0; nn < 13; nn++) {
							
							if (bconds[(lastdigit * 13) + nn] == 2) {
								
								bconds[(lastdigit * 13) + nn] = 0;
								
							}
							
							if (nn == configs.indexOf(c)) {
								
								bconds[(lastdigit * 13) + nn] = 1;
								
							}
							
						}
						
					}
					
				}
				
				if (survival) {
					
					if (negating) {
						
						sconds[(lastdigit * 13) + configs.indexOf(c)] = 0;
						
					} else {
						
						for (int nn = 0; nn < 13; nn++) {
							
							if (nn == configs.indexOf(c)) {
								
								sconds[(lastdigit * 13) + nn] = 1;
								
							} else if (sconds[(lastdigit * 13) + nn] == 2) {
								
								sconds[(lastdigit * 13) + nn] = 0;
								
							}
							
						}
						
					}
					
				}
				
			}
			
			if (c == '-') {
				
				negating = true;
				
			}
			
		}
		
		int[][] conds = {bconds, sconds};
		
		return conds;
		
	}
	
	void parse() {
		
		rparse();
		
	}
	
	// File functions
	
	int getcurrentid() throws FileNotFoundException, IOException {
		
		int c;
		
		int id = 0;
		
		FileInputStream file = new FileInputStream(idpath + "ngliders.txt");
		
		c = file.read();
		
		while (c != -1) {
			
			id *= 10;
			
			id += (c - '0');
			
			c = file.read();
			
		}
		
		file.close();
		
		return id;
		
	}
	
	int eparse_dx, eparse_dy, eparse_period, eparse_minpop, eparse_id;
	
	String eparse_minrule = "", eparse_maxrule = "", eparse_rle = "";
	
	void eparse() throws FileNotFoundException, IOException {
		
		int c;
		
		int linecount = 0;
		
		this.eparse_dx = 0;
		
		this.eparse_dy = 0;
		
		this.eparse_period = 0;
		
		this.eparse_minpop = 0;
		
		this.eparse_id = 0;
		
		this.eparse_minrule = "";
		
		this.eparse_maxrule = "";
		
		this.eparse_rle = "";
		
		// FileInputStream file = new FileInputStream(gliders + "GlidINT.txt");
		
		c = this.nextg.read();
		
		while (c != ' ') {
			
			this.eparse_dy *= 10;
			
			this.eparse_dy += (c - '0');
			
			c = this.nextg.read();
			
		}
		
		c = this.nextg.read(); // Gets ':'
		
		c = this.nextg.read(); // Gets ' '
		
		c = this.nextg.read();
		
		while (c != ' ') {
			
			this.eparse_dx *= 10;
			
			this.eparse_dx += (c - '0');
			
			c = this.nextg.read();
			
		}
		
		// System.out.println(this.eparse_dx);
		
		c = this.nextg.read(); // Gets ':'
		
		c = this.nextg.read(); // Gets ' '
		
		c = this.nextg.read();
		
		while (c != ' ') {
			
			this.eparse_period *= 10;
			
			this.eparse_period += (c - '0');
			
			c = this.nextg.read();
			
		}
		
		c = this.nextg.read(); // Gets ':'
		
		c = this.nextg.read(); // Gets ' '
		
		c = this.nextg.read();
		
		while (c != ' ') {
			
			this.eparse_minpop *= 10;
			
			this.eparse_minpop += (c - '0');
			
			c = this.nextg.read();
			
		}
		
		c = this.nextg.read(); // Gets ':'
		
		c = this.nextg.read(); // Gets ' '
		
		c = this.nextg.read();
		
		while (c != ' ') {
			
			this.eparse_minrule += (char) (c);
			
			c = this.nextg.read();
			
		}
		
		c = this.nextg.read(); // Gets ':'
		
		c = this.nextg.read(); // Gets ' '
		
		c = this.nextg.read();
		
		while (c != ' ') {
			
			this.eparse_maxrule += (char) (c);
			
			c = this.nextg.read();
			
		}
		
		c = this.nextg.read(); // Gets ':'
		
		c = this.nextg.read(); // Gets ' '
		
		c = this.nextg.read();
		
		while (c != ' ') {
			
			this.eparse_rle += (char) (c);
			
			c = this.nextg.read();
			
		}
		
		c = this.nextg.read(); // Gets ':'
		
		c = this.nextg.read(); // Gets ' '
		
		c = this.nextg.read();
		
		while (c != ',') {
			
			this.eparse_id *= 10;
			
			this.eparse_id += (c - '0');
			
			c = this.nextg.read();
			
		}
		
		c = this.nextg.read();
		
		// this.nextg.close();
		
		// System.out.println(this.eparse_dy + ":" + this.eparse_dx + ":" + this.eparse_period + ":" + this.eparse_minrule + ":" + this.eparse_maxrule + ":" + this.eparse_rle);
		
	}
	
	final int ABOVE = 0;
	
	final int BELOW = 0;
	
	final int EQUAL = 1; // The current rule is equal to the one being tested against
	
	final int OTHER = 2; // The current rule is not inside the min/max range
	
	int abovelower(int[] bconds, int[] sconds, int[] lbconds, int[] lsconds) {
		
		int tag = 0;
		
		int status = EQUAL;
		
		int[] limits = {1, 2, 6, 10, 13, 10, 6, 2, 1};
		
		for (int n = 0; n < 117; n++) {
			
			if ((n % 13) >= limits[n / 13]) continue;
			
			if ((bconds[n] != 0) && (lbconds[n] == 0) && (status == EQUAL)) status = ABOVE;
			
			if ((bconds[n] == 0) && (lbconds[n] != 0) && (status != OTHER)) status = OTHER;
			
		}
		
		for (int n = 0; n < 117; n++) {
			
			if ((n % 13) >= limits[n / 13]) continue;
			
			if ((sconds[n] != 0) && (lsconds[n] == 0) && (status == EQUAL)) status = ABOVE;
			
			if ((sconds[n] == 0) && (lsconds[n] != 0) && (status != OTHER)) status = OTHER;
			
		}
		
		return status;
		
	}
	
	int belowupper(int[] bconds, int[] sconds, int[] ubconds, int[] usconds) {
		
		int tag = 0;
		
		int status = EQUAL;
		
		int[] limits = {1, 2, 6, 10, 13, 10, 6, 2, 1};
		
		for (int n = 0; n < 117; n++) {
			
			if ((n % 13) >= limits[n / 13]) continue;
			
			if ((bconds[n] == 0) && (ubconds[n] != 0) && (status == EQUAL)) status = BELOW;
			
			if ((bconds[n] != 0) && (ubconds[n] == 0)) status = OTHER;
			
		}
		
		for (int n = 0; n < 117; n++) {
			
			if ((n % 13) >= limits[n / 13]) continue;
			
			if ((sconds[n] == 0) && (usconds[n] != 0) && (status == EQUAL)) status = BELOW;
			
			if ((sconds[n] != 0) && (usconds[n] == 0)) status = OTHER;
			
		}
		
		return status;
		
	}
	
	int countlines() throws FileNotFoundException, IOException {
		
		int lines = 0;
		
		int c;
		
		FileInputStream file = new FileInputStream(gliders + "/GlidINT.txt");
		
		c = file.read();
		
		while (c != -1) {
			
			if (c == '\n') lines++;
			
			c = file.read();
			
		}
		
		file.close();
		
		return lines;
		
	}
	
	void searchdb() throws FileNotFoundException, IOException {
		
		// int maxid = getcurrentid();
		
		int lines = countlines();
		
		// int processed;
		
		int[] lbconds = new int[117], lsconds = new int[117];
		
		int[] ubconds = new int[117], usconds = new int[117];
		
		int[][] lconds = new int[2][117];
		
		int[][] uconds = new int[2][117];
		
		for (int n = 1; n < (lines + 1); n++) {
			
			// processed = process(n);
				
			// if (processed == -1) continue;
			
			// System.out.println("Processing...");
			
			eparse();
			
			lconds = rparse(this.eparse_minrule);
			
			uconds = rparse(this.eparse_maxrule);
			
			ubconds = uconds[0];
			
			usconds = uconds[1];
			
			lbconds = lconds[0];
			
			lsconds = lconds[1];
			
			if ((abovelower(this.bconds, this.sconds, lbconds, lsconds) == OTHER) || (belowupper(this.bconds, this.sconds, ubconds, usconds) == OTHER)) continue;
			
			System.out.println("#C GlidINT " + this.eparse_id);
			
			System.out.println("#C Works from " + this.eparse_minrule + " to " + this.eparse_maxrule);
			
			System.out.println("#C Systematic name: " + this.eparse_minpop + "P" + this.eparse_period + "H" + this.eparse_dy + "V" + this.eparse_dx);
			
			System.out.println("x = 0, y = 0, rule = " + this.rulestring);
			
			System.out.println(this.eparse_rle);
			
		}
		
	}
	
}

class SearchDB {
	
	public static void main(String[] args) throws FileNotFoundException, IOException, InterruptedException {
		
		if (args.length == 0) {
			
			System.out.println("Usage: java SearchDB <rulestring>");
			
			return;
			
		}
		
		var parser = new Parser();
		
		parser.parse(args[0]);
		
		var funcs = new Funcs(parser.rulestring);
		
		System.out.println("Updating local GlidINT database copy...");
		
		Process process1 = new ProcessBuilder("curl", "-o", (funcs.gliders + "GlidINT.txt"), "https://sim.zpsou.eu/interna242/~mamaral/gliders/GlidINT.txt").inheritIO().start();
		
		Process process2 = new ProcessBuilder("curl", "-o", (funcs.idpath + "ngliders.txt"), "https://sim.zpsou.eu/interna242/~mamaral/gliders/ngliders.txt").inheritIO().start();
		
		process1.waitFor();
		
		process2.waitFor();
		
		funcs.parse();
		
		funcs.searchdb();
		
	}
	
}
To use it, make a file called SearchDB.java, then edit the "gliders" and "idpath" variables to the directory where you want "GlidINT.txt" (the database itself) and "ngliders.txt" (the next glider ID to be used) to be saved.

Then, run

Code: Select all

$ javac SearchDB.java
in the directory where you saved the SearchDB file.

After that, run

Code: Select all

$ java SearchDB <rulestring>
where "rulestring" is the rulestring of the rule whose gliders you want to search for.

The output should look something like this:

Code: Select all

$ java SearchDB B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak
Updating local GlidINT database copy...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100     2  100     2    0     0      7      0 --:--:-- --:--:-- --:--:--     7
100 20334  100 20334    0     0  47620      0 --:--:-- --:--:-- --:--:-- 47509
#C GlidINT 7
#C Works from B3-ry4acenqt5eir6acn/S2-a3-a4nq5aknr6cei to B3-ry4acenqt5eir6-ek8/S2-a3-a4cnq5-ijqy6-ak
#C Systematic name: 109P5H2V1
x = 0, y = 0, rule = B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak
2bo$o3bo$2o4b2o$o2b4o$2b2obo$4b2o$4bobo6bo$6bob2obob2o$6bo6bo$10b2obo$8b2o3bo$10b5o$10bo2bo2b2o$12b5o$17bo$18bo$16b3o$19b2o$14bo5bo$14b2o2bob2o$14bo3b2o2bo$16b3o3b3o$21bo$21bo3bo$21b3o$23bo$23b2o$24bo6bobo$26b3o4bo$25b2o3bob2o$23bo5bob2o$23bob2o3bo$24b3ob2o$25b3o!
#C GlidINT 8
#C Works from B3aijq4a/S2eik3ijnr to B2cin345-a67e8/S2-a3-a4-akw5-qy6-k78
#C Systematic name: 8P4H2V0
x = 0, y = 0, rule = B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak
bo$3bo$3bo$o2bo$b3o!
My website (a database of Life objects, WIP).
GlidINT has been released!
Currently working on a forum system for my website.

Code: Select all

x = 5, y = 17, rule = B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-akHistory
C$2C$.2C$2C11$.3D$3.D$2.3D!
[[ STOP 72 ]]
User avatar
MDA
Posts: 294
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

Re: INT version of gliderdb

Post by MDA »

GlidINT now has an attribution system. To use it, here is a Curl-dependent Python script for uploading an attribution to the database (you need to know the GlidINT ID of the spaceship whose attribution you intend to upload):

Code: Select all

from random import randint

from subprocess import run

ident = int(input("Spaceship ID: "))

name = input("Spaceship name: ")

person = input("Discoverer: ")

prog = input("Program used: ")

yearstr = input("Discovery year (hit Enter for unknown): ")

monthstr = ""

daystr = ""

year = 0

month = 0

day = 0

maxdays = [31, 00, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

if yearstr != "":
	
	year = int(yearstr)
	
	monthstr = input("Discovery month (hit Enter for unknown): ")
	
	if monthstr != "":
		
		month = int(monthstr)
		
		daystr = input("Discovery day (hit Enter for unknown): ")
		
		if daystr == "":
			
			day = 0
			
		else:
			
			day = int(daystr)
			
	else:
		
		month = 0
		
else:
	
	year = 0
	
if ident <= 0:
	
	raise ValueError("Nonexistent spaceship ID")
	
if month > 12 or month < 0 or day < 0:
	
	raise ValueError("Invalid date")
	
if month == 2:
	
	if (year % 400 == 0) or ((year % 4 == 0) and (year % 100 != 0)):
		
		if day > 29:
			
			raise ValueError("Invalid date")
			
	else:
		
		if day > 28:
			
			raise ValueError("Invalid date")
			
else:
	
	if (day > maxdays[month]):
		
		raise ValueError("Invalid date")
		
r = randint(0, 100000000000000)

path = "/Users/YOUR USERNAME HERE/tmp"

with open(f"{path}/{r}.attr", "w") as file:
	
	file.write(f"{ident}:{name}:{person}:{prog}:{year}:{month}:{day},\n")
	
run(f"/usr/local/bin/curl -T {path}/{r}.attr https://sim.zpsou.eu/interna242/~mamaral/gliders/uncommitted/{r}.attr", shell = True)
In order to find the ID of the spaceship whose attribution you want to submit, use the database searcher included in the last post.

Spaceship attributions can be overwritten if inaccuracies are present.
My website (a database of Life objects, WIP).
GlidINT has been released!
Currently working on a forum system for my website.

Code: Select all

x = 5, y = 17, rule = B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-akHistory
C$2C$.2C$2C11$.3D$3.D$2.3D!
[[ STOP 72 ]]
Post Reply