NaiViewer - a program for simulating outer-totalistic naiverules

For scripts to aid with computation or simulation in cellular automata.
User avatar
MDA
Posts: 291
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

Re: NaiViewer - a program for simulating INT naiverules

Post by MDA »

Version 2 of NaiViewer is out:
NaiViewer.zip
This is version 2.0 of NaiViewer (not a beta).
(5.33 KiB) Downloaded 3 times
Compile with

Code: Select all

cc naiviewer.c -o naiviewer $(pkg-config --cflags --libs raylib)
Improvements:

- Added selecting, copying, cutting, and pasting.

- Added selection randomization, inversion, and deletion.

- Added event counter to prevent the program from running too fast.

Edit (July 5th, 2026, ~2:17 PM):
MDA wrote: July 4th, 2026, 2:58 pm
I6_I6 wrote: July 4th, 2026, 10:28 am […] I could put NaiViewer on GitHub, if you'd like.
Not yet. I want to put it on GitHub myself, but only once the new version gains all the features from version 1.

[…]
NaiViewer now has its own GitHub repository. Feel free to contribute if you have anything to add.
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
I6_I6
Posts: 1092
Joined: July 26th, 2025, 8:44 pm
Location: Here, there, somewhere, anywhere, everywhere.
Contact:

Re: NaiViewer - a program for simulating outer-totalistic naiverules

Post by I6_I6 »

Why is there a blank line every other line? Is it just my system?

Code: Select all

#C [[ THEME Golly ]]
x = 27, y = 15, rule = LifeHistory
8.A$A6.A.A$3A4.BA2B.B2D$3.A4.2B.2B2DB$2.2A2.3B.6B2.3B$2.20B$4.19B$4.2B
C10BD4B$4.2B2C10BD4B$4.B2C11B2D3B$4.13B2D4B$5.12BD3B.B2A$6.13B3.BA.A$
6.3B.B3.B10.A$25.2A!
RuleEdit: A .rule file editing tool
User avatar
MDA
Posts: 291
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

Re: NaiViewer - a program for simulating INT naiverules

Post by MDA »

I6_I6 wrote: July 5th, 2026, 10:36 am Why is there a blank line every other line? Is it just my system?
No, it's not your system, just a style choice of mine.
Last edited by MDA on July 9th, 2026, 10: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: 291
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

Re: NaiViewer - a program for simulating INT naiverules

Post by MDA »

I am proud to announce that NaiViewer now has official support for Generations in both Naive and non-naive rules. To use this new feature, just add a slash after the rulestring and write G and the number of states. For example, B2/S345/G4 is Star Wars.
Last edited by MDA on July 9th, 2026, 10: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
I6_I6
Posts: 1092
Joined: July 26th, 2025, 8:44 pm
Location: Here, there, somewhere, anywhere, everywhere.
Contact:

Re: NaiViewer - a program for simulating outer-totalistic naiverules

Post by I6_I6 »

MDA wrote: July 8th, 2026, 3:44 pm I am proud to announce that NaiViewer now has official support for Generations in both Naive and non-naive rules. To use this new feature, just add a slash after the rulestring and write G and the number of states. For example, B2/S345/G4 is Star Wars.
Generations rulestrings usually don't have a G before the number of states.
(Edit: Fixed typo)
Last edited by I6_I6 on July 9th, 2026, 2:37 am, edited 1 time in total.

Code: Select all

#C [[ THEME Golly ]]
x = 27, y = 15, rule = LifeHistory
8.A$A6.A.A$3A4.BA2B.B2D$3.A4.2B.2B2DB$2.2A2.3B.6B2.3B$2.20B$4.19B$4.2B
C10BD4B$4.2B2C10BD4B$4.B2C11B2D3B$4.13B2D4B$5.12BD3B.B2A$6.13B3.BA.A$
6.3B.B3.B10.A$25.2A!
RuleEdit: A .rule file editing tool
User avatar
MDA
Posts: 291
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

Re: NaiViewer - a program for simulating INT naiverules

Post by MDA »

I6_I6 wrote: July 9th, 2026, 2:31 am
MDA wrote: July 8th, 2026, 3:44 pm […] To use [Generations rules], just add a slash after the rulestring and write G and the number of states. For example, B2/S345/G4 is Star Wars.
Generations rulestrngs [sic] usually don't have a G before the number of states.
I know, but in this case, it was easier for me to implement them using a format similar to B/S notation because that was what the parser was originally designed to handle.
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: 291
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

Re: NaiViewer - a program for simulating INT naiverules

Post by MDA »

NaiViewer now has official support for naive and non-naive INT BSFKL rules.

Feel free to report any bugs or major discoveries made using this program here.
Last edited by MDA on July 14th, 2026, 2:41 pm, 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
NNlk05
Posts: 767
Joined: January 14th, 2026, 8:42 pm
Location: Exploring in the Jungle of the INT Rulespace
Contact:

Re: NaiViewer - a program for simulating outer-totalistic naiverules

Post by NNlk05 »

Where can I find the latest version of the code?
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: 291
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

Re: NaiViewer - a program for simulating INT naiverules

Post by MDA »

NNlk05 wrote: July 10th, 2026, 8:39 am Where can I find the latest version of the code?
You can find the latest version of the code here.
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
I6_I6
Posts: 1092
Joined: July 26th, 2025, 8:44 pm
Location: Here, there, somewhere, anywhere, everywhere.
Contact:

Re: NaiViewer - a program for simulating INT naiverules

Post by I6_I6 »

MDA wrote: July 10th, 2026, 2:49 am NaiViewer now has official support for naive and non-naive INT BSFKL rules.

Feel free to report any bugs or major discoveries made using this program here.
Why the non-naive INT BSFKL rules? It's nice, since I don't know of any other programs that natively support it, but it isn't a naiverule. (Same goes for generations.) When inputting the rule, how can the software tell whether the user wants the rule to be naive or not?

Code: Select all

#C [[ THEME Golly ]]
x = 27, y = 15, rule = LifeHistory
8.A$A6.A.A$3A4.BA2B.B2D$3.A4.2B.2B2DB$2.2A2.3B.6B2.3B$2.20B$4.19B$4.2B
C10BD4B$4.2B2C10BD4B$4.B2C11B2D3B$4.13B2D4B$5.12BD3B.B2A$6.13B3.BA.A$
6.3B.B3.B10.A$25.2A!
RuleEdit: A .rule file editing tool
User avatar
MDA
Posts: 291
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

Re: NaiViewer - a program for simulating INT naiverules

Post by MDA »

I6_I6 wrote: August 7th, 2026, 8:08 am
MDA wrote: July 10th, 2026, 2:49 am NaiViewer now has official support for naive and non-naive INT BSFKL rules.

[...]
Why the non-naive INT BSFKL rules? It's nice, since I don't know of any other programs that natively support it, but it isn't a naiverule. (Same goes for generations.) When inputting the rule, how can the software tell whether the user wants the rule to be naive or not?
To specify naive versions of rules, just prefix the rulestring with an uppercase N. NaiveLife becomes NB3/S23 and Arthroid becomes NB3678/S24578.

Replying to your other question, I first wrote NaiViewer to focus exclusively on naiverules, but later added support for regular rules because I wanted to add them.
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: 291
Joined: June 8th, 2026, 12:18 pm
Location: In Haven (B3-ry4acenqt5eir6-ek/S2-a3-a4nq5aeknr6-ak)
Contact:

Re: NaiViewer - a program for simulating outer-totalistic naiverules

Post by MDA »

I just finished writing an improved Java version of NaiViewer with support for deficient rules and anti-strobing algorithms:

Code: Select all

import java.awt.*;

import java.awt.datatransfer.*;

import java.awt.event.*;

import java.util.Random;

class Funcs extends Frame {
	
	int statenum = 2;
	
	int[] bconds, sconds, dconds, fconds, kconds, lconds;
	
	int[] cursor;
	
	int[][] region;
	
	int[][] world;
	
	boolean tag = true;
	
	boolean color = false;
	
	boolean naive = false;
	
	boolean selecting = false;
	
	boolean carrying = true;
	
	String rulestring = "";
	
	enum Mode {
	    
	    Running, SettingRule;
	    
	}
	
	Mode mode = Mode.Running;
	
	enum Ruletype {
		
		INT, Generations, Deficient, BSFKL;
		
	}
	
	Ruletype ruletype = Ruletype.INT;
	
	public Funcs() {
		
		bconds = new int[117];
		
		sconds = new int[117];
		
		dconds = new int[117];
		
		fconds = new int[117];
		
		kconds = new int[117];
		
		lconds = new int[117];
		
		region = new int[2][2];
		
		world = new int[200][200];
		
		cursor = new int[2];
		
		cursor[0] = 100;
		
		cursor[1] = 100;
		
		region[0][0] = 100;
		
		region[0][1] = 100;
		
		region[1][0] = 100;
		
		region[1][1] = 100;
		
		addWindowListener(new WindowAdapter() {
			
			public void windowClosing(WindowEvent we) {
				
				System.exit(0);
				
			}
			
		});
		
		addKeyListener(new KeyAdapter() {
			
			public void keyPressed(KeyEvent ke) {
				
				int key = ke.getKeyCode();
				
				if (mode == Mode.Running) {
						    
					int minx = (region[0][0] < region[1][0]) ? region[0][0] : region[1][0];
					
					int maxx = (region[0][0] > region[1][0]) ? region[0][0] : region[1][0];
					
					int miny = (region[0][1] < region[1][1]) ? region[0][1] : region[1][1];
					
					int maxy = (region[0][1] > region[1][1]) ? region[0][1] : region[1][1];
					
					String pattern;
					
					Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
					
					switch (key) {
						
						case KeyEvent.VK_C:
						    
						    pattern = "";
						    
						    if ((minx == maxx) || (miny == maxy)) {
							    
							    for (int x = 0; x < 200; x++) {
							        
							        for (int y = 0; y < 200; y++) {
							               
							            pattern += world[x][y];
							            
							            pattern += " ";
							            
							        }
							        
							        pattern += "\n";
							        
							    }
							    
							} else {
								
							    for (int x = minx; x < maxx; x++) {
							        
							        for (int y = miny; y < maxy; y++) {
							               
							            pattern += world[x][y];
							            
							            pattern += " ";
							            
							        }
							        
							        pattern += "\n";
							        
							    }
								
							}
							
						    StringSelection selection = new StringSelection(pattern);
						    
						    clipboard.setContents(selection, null);
						    
						    break;
							
						case KeyEvent.VK_I:
						    
						    if ((minx == maxx) || (miny == maxy)) {
							    
							    for (int x = 0; x < 200; x++) {
							        
							        for (int y = 0; y < 200; y++) {
							               
							            world[x][y] = (world[x][y] == 0) ? 1 : 0;
							            
							        }
							        
							    }
							    
							} else {
								
							    for (int x = minx; x < maxx; x++) {
							        
							        for (int y = miny; y < maxy; y++) {
							               
							            world[x][y] = (world[x][y] == 0) ? 1 : 0;
							            
							        }
							        
							    }
								
							}
							
						    repaint();
						    
						    break;
							
						case KeyEvent.VK_K:
							
							if (color) {
								
								color = false;
								
								for (int x = 0; x < 200; x++) {
									
									for (int y = 0; y < 200; y++) {
										
										if (world[x][y] >= statenum) world[x][y] = statenum - 1;
										
									}
									
								}
								
							}
							
							else color = true;
							
							break;
							
						case KeyEvent.VK_R:
						    
						    int c = 2;
						    
						    Random random = new Random();
						    
						    if ((ruletype == Ruletype.Deficient) || (ruletype == Ruletype.BSFKL)) c = 3;
						    
						    if ((minx == maxx) || (miny == maxy)) {
							    
							    for (int x = 0; x < 200; x++) {
							        
							        for (int y = 0; y < 200; y++) {
							               
							            world[x][y] = random.nextInt(c);
							            
							        }
							        
							    }
							    
							} else {
								
							    for (int x = minx; x < maxx; x++) {
							        
							        for (int y = miny; y < maxy; y++) {
							               
							            world[x][y] = random.nextInt(c);
							            
							        }
							        
							    }
								
							}
							
						    repaint();
						    
						    break;
						    
						case KeyEvent.VK_T:
							
							mode = Mode.SettingRule;
							
							tag = true;
							
							break;
							
						case KeyEvent.VK_V:
							
							int cx = cursor[1], cy = cursor[0];
							
							int state = 0;
							
							try {
								
								if (clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
									
									pattern = (String) clipboard.getData(DataFlavor.stringFlavor);
									
									for (int n = 0; n < pattern.length(); n++) {
										
										char cc = pattern.charAt(n);
										
										switch (cc) {
											
											case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> {
												
												state *= 10;
												
												state += (cc - '0');
												
											}
											
											case ' ' -> {
												
												if (color) world[cy][cx] = (state == 0) ? 0 : 1;
												
												else world[cy][cx] = (state >= statenum) ? statenum - 1 : state;
												
												state = 0;
												
												cx++;
												
												cx %= 200;
												
											}
											
											case '\n' -> {
												
												cy++;
												
												cy %= 200;
												
												cx = cursor[1];
												
											}
											
										}
										
									}
									
								}
								
							} catch (Exception e) {
								
							}
							
							repaint();
							
							break;
							
						case KeyEvent.VK_0:
							
							selecting = false;
							
							carrying = true;
							
							region[0][0] = cursor[0];
							
							region[0][1] = cursor[1];
							
							region[1][0] = cursor[0];
							
							region[1][1] = cursor[1];
							
							repaint();
							
							break;
							
						case KeyEvent.VK_UP:
							
							cursor[0] += 199;
						    
						    cursor[0] %= 200;
						    
						    if (selecting) {
						    		
						    		region[1][0] = cursor[0];
						    		
						    		region[1][1] = cursor[1];
						    		
						    	} else {
						    		
						    		if (carrying) {
						    			
						    			region[0][0] = cursor[0];
						    			
						    			region[0][1] = cursor[1];
						    			
						    			region[1][0] = cursor[0];
						    			
						    			region[1][1] = cursor[1];
									
								}
								
							}
							
						    repaint();
							
							break;
							
						case KeyEvent.VK_DOWN:
							
							cursor[0] += 1;
						    
						    cursor[0] %= 200;
						    
						    if (selecting) {
						    		
						    		region[1][0] = cursor[0];
						    		
						    		region[1][1] = cursor[1];
						    		
						    	} else {
						    		
						    		if (carrying) {
						    			
						    			region[0][0] = cursor[0];
						    			
						    			region[0][1] = cursor[1];
						    			
						    			region[1][0] = cursor[0];
						    			
						    			region[1][1] = cursor[1];
									
								}
								
							}
						    
						    repaint();
							
							break;
							
						case KeyEvent.VK_LEFT:
							
							cursor[1] += 199;
						    
						    cursor[1] %= 200;
						    
						    if (selecting) {
						    		
						    		region[1][0] = cursor[0];
						    		
						    		region[1][1] = cursor[1];
						    		
						    	} else {
						    		
						    		if (carrying) {
						    			
						    			region[0][0] = cursor[0];
						    			
						    			region[0][1] = cursor[1];
						    			
						    			region[1][0] = cursor[0];
						    			
						    			region[1][1] = cursor[1];
									
								}
								
							}
						    
						    repaint();
							
							break;
							
						case KeyEvent.VK_RIGHT:
							
							cursor[1] += 1;
						    
						    cursor[1] %= 200;
						    
						    if (selecting) {
						    		
						    		region[1][0] = cursor[0];
						    		
						    		region[1][1] = cursor[1];
						    		
						    	} else {
						    		
						    		if (carrying) {
						    			
						    			region[0][0] = cursor[0];
						    			
						    			region[0][1] = cursor[1];
						    			
						    			region[1][0] = cursor[0];
						    			
						    			region[1][1] = cursor[1];
									
								}
								
							}
						    
						    repaint();
							
							break;
							
						case KeyEvent.VK_SPACE:
						    
						    advance();
						    
						    if ((!naive) && (bconds[0] != 0) && (sconds[104] == 0)) { // Prevents strobing in B0 rules
						    		
						    		if ((ruletype == Ruletype.INT) || (ruletype == Ruletype.Deficient)) advance();
						    		
						    		if (ruletype == Ruletype.Generations) for (int n = 1; n < statenum; n++) advance();
						    		
						    	}
						    	
						    	if ((!naive) && (ruletype == Ruletype.BSFKL) && (bconds[0] != 0) && (fconds[0] != 0) && (kconds[0] != 0)) {
						    		
						    		advance();
						    		
						    	}
						    	
						    	if ((!naive) && (ruletype == Ruletype.BSFKL) && (bconds[0] != 0) && (sconds[104] == 0) && (fconds[0] != 0) && (kconds[0] == 0) && (lconds[0] != 0)) {
						    		
						    		advance();
						    		
						    		advance();
						    		
						    	}
						    	
						    repaint();
						    
						    break;
						    
						case KeyEvent.VK_BACK_SPACE:
						    
						    if ((minx == maxx) || (miny == maxy)) {
							    
							    world = new int[200][200];
							    
							} else {
								
							    for (int x = minx; x < maxx; x++) {
							        
							        for (int y = miny; y < maxy; y++) {
							               
							            world[x][y] = 0;
							            
							        }
							        
							    }
								
							}
						    
						    repaint();
						    
						    break;
						    
						case KeyEvent.VK_ENTER:
						    
						    world[cursor[0]][cursor[1]]++;
						    
						    world[cursor[0]][cursor[1]] %= statenum;
						    
						    // repaint(cursor[0] * 4, cursor[1] * 4, 4, 4);
						    
						    repaint();
						    
						    break;
						    
						case KeyEvent.VK_SHIFT:
							
							if (selecting) {
								
								selecting = false;
								
								carrying = false;
								
							}
							
							else selecting = true;
							
							break;
							
					}
					
				}
				
				if (mode == Mode.SettingRule) {
					
					switch (key) {
						
						case KeyEvent.VK_ENTER:
							
							bconds = new int[117];
							
							sconds = new int[117];
							
							dconds = new int[117];
							
							fconds = new int[117];
							
							kconds = new int[117];
							
							lconds = new int[117];
							
							if (rulestring.equals("R")) {
								
								int type;
								
								Random random = new Random();
								
								if (random.nextInt(2) == 0) naive = false;
								
								else naive = true;
								
								type = random.nextInt(4);
								
								switch (type) {
									
									case 0 -> {
										
										ruletype = Ruletype.INT;
										
										for (int n = 0; n < 117; n++) {
											
											bconds[n] = random.nextInt(2);
											
											sconds[n] = random.nextInt(2);
											
										}
										
									}
									
									case 1 -> {
										
										ruletype = Ruletype.Generations;
										
										statenum = random.nextInt(9) + 2;
										
										for (int n = 0; n < 117; n++) {
											
											bconds[n] = random.nextInt(2);
											
											sconds[n] = random.nextInt(2);
											
										}
										
									}
									
									case 2 -> {
										
										ruletype = Ruletype.Deficient;
										
										for (int n = 0; n < 117; n++) {
											
											bconds[n] = random.nextInt(2);
											
											sconds[n] = random.nextInt(2);
											
											dconds[n] = random.nextInt(2);
											
										}
										
									}
									
									case 3 -> {
										
										ruletype = Ruletype.BSFKL;
										
										for (int n = 0; n < 117; n++) {
											
											bconds[n] = random.nextInt(2);
											
											sconds[n] = random.nextInt(2);
											
											fconds[n] = random.nextInt(2);
											
											kconds[n] = random.nextInt(2);
											
											lconds[n] = random.nextInt(2);
											
										}
										
									}
									
								}
								
								rulestring = writerule();
								
							}
							
							int oldstatenum = statenum;
							
							rparse(rulestring);
							
							if (((ruletype != Ruletype.INT) && color) || (oldstatenum > statenum)) {
								
								for (int x = 0; x < 200; x++) {
									
									for (int y = 0; y < 200; y++) {
										
										if (world[y][x] > 1) world[y][x] = 1;
										
									}
									
								}
								
								color = false;
								
							}
							
							rulestring = "";
							
							mode = Mode.Running;
							
							setTitle("NaiViewer (" + writerule() + ")");
							
							repaint();
							
							break;
							
						case KeyEvent.VK_BACK_SPACE:
							
							String tempstring = "";
							
							for (int n = 0; n < (rulestring.length() - 1); n++) {
								
								tempstring += rulestring.charAt(n);
								
							}
							
							rulestring = tempstring;
							
							repaint();
							
							break;
							
					}
					
				}
				
			}
			
			public void keyTyped(KeyEvent ke) {
				
				if (mode == Mode.SettingRule) {
					
					if (ke.getKeyChar() != 8) rulestring += ke.getKeyChar(); // Prevents 'delete' characters from being appended to the rulestring
					
					if (tag) {
						
						rulestring = "";
						
						tag = false;
						
					}
					
					repaint();
					
				}
				
			}
			
		});
		
		addMouseMotionListener(new MouseAdapter() {
			
			public void mouseDragged(MouseEvent me) {
				
				switch (mode) {
					
					case Running:
						
						if (world[me.getY() / 4][me.getX() / 4] == 0) world[me.getY() / 4][me.getX() / 4] = 1;
						
						else world[me.getY() / 4][me.getX() / 4] = 0;
						
						cursor[0] = me.getY() / 4;
						
						cursor[1] = me.getX() / 4;
						
						if (selecting) {
						   	
							region[1][0] = cursor[0];
							
							region[1][1] = cursor[1];
							
						} else {
							
							if (carrying) {
								
								region[0][0] = cursor[0];
								
								region[0][1] = cursor[1];
								
								region[1][0] = cursor[0];
								
								region[1][1] = cursor[1];
								
							}
							
						}
						
						repaint();
						
						break;
						
				}
				
			}
			
		});
		
		addMouseListener(new MouseAdapter() {
			
			public void mouseClicked(MouseEvent me) {
				
				switch (mode) {
					
					case Running:
						
						if (world[me.getY() / 4][me.getX() / 4] == 0) world[me.getY() / 4][me.getX() / 4] = 1;
						
						else world[me.getY() / 4][me.getX() / 4] = 0;
						
						cursor[0] = me.getY() / 4;
						
						cursor[1] = me.getX() / 4;
						
						if (selecting) {
						   	
							region[1][0] = cursor[0];
							
							region[1][1] = cursor[1];
							
						} else {
							
							if (carrying) {
								
								region[0][0] = cursor[0];
								
								region[0][1] = cursor[1];
								
								region[1][0] = cursor[0];
								
								region[1][1] = cursor[1];
								
							}
							
						}
						
						repaint();
						
						break;
						
				}
				
			}
			
		});
		
	}
	
	void rparse(String rulestring) {
		
		int lastdigit = -1;
		
		char c;
		
		boolean negating = false;
		
		String letters = "cekainyqjrtwz";
		
		enum Set {
			
			birth, survival, generations, deficient, f, k, l;
			
		}
		
		Set set = Set.birth;
		
		this.naive = false;
		
		this.statenum = 2;
		
		this.ruletype = Ruletype.INT;
		
		for (int n = 0; n < rulestring.length(); n++) {
			
			c = rulestring.charAt(n);
			
			switch (c) {
				
				case 'B' -> set = Set.birth;
				
				case 'D' -> {
					
					this.statenum = 3;
					
					set = Set.deficient;
					
					ruletype = Ruletype.Deficient;
					
				}
				
				case 'F' -> {
					
					this.statenum = 3;
					
					set = Set.f;
					
					ruletype = Ruletype.BSFKL;
					
				}
				
				case 'G' -> {
					
					this.statenum = 0;
					
					set = Set.generations;
					
					ruletype = Ruletype.Generations;
					
				}
				
				case 'K' -> {
					
					this.statenum = 3;
					
					set = Set.k;
					
					ruletype = Ruletype.BSFKL;
					
				}
				
				case 'L' -> {
					
					this.statenum = 3;
					
					set = Set.l;
					
					ruletype = Ruletype.BSFKL;
					
				}
				
				case 'N' -> this.naive = true;
				
				case 'S' -> set = Set.survival;
					
				case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> {
					
					if (set != Set.generations) {
						
						lastdigit = (c - '0');
						
						for (int nn = 0; nn < 13; nn++) {
							
							switch (set) {
								
								case birth -> this.bconds[(lastdigit * 13) + nn] = 2;
								
								case survival -> this.sconds[(lastdigit * 13) + nn] = 2;
								
								case deficient -> this.dconds[(lastdigit * 13) + nn] = 2;
								
								case f -> this.fconds[(lastdigit * 13) + nn] = 2;
								
								case k -> this.kconds[(lastdigit * 13) + nn] = 2;
								
								case l -> this.lconds[(lastdigit * 13) + nn] = 2;
								
							}
							
						}
						
					} else {
						
						this.statenum *= 10;
						
						this.statenum += (c - '0');
						
					}
					
					negating = false;
					
				}
				
				case 'c', 'e', 'k', 'a', 'i', 'n', 'y', 'q', 'j', 'r', 't', 'w', 'z' -> {
					
					int i = letters.indexOf(c);
					
					if (negating) {
						
						switch (set) {
							
							case birth -> this.bconds[(lastdigit * 13) + i] = 0;
							
							case survival -> this.sconds[(lastdigit * 13) + i] = 0;
							
							case deficient -> this.dconds[(lastdigit * 13) + i] = 0;
							
							case f -> this.fconds[(lastdigit * 13) + i] = 0;
							
							case k -> this.kconds[(lastdigit * 13) + i] = 0;
							
							case l -> this.lconds[(lastdigit * 13) + i] = 0;
							
						}
						
					} else {
						
						for (int nn = 0; nn < 13; nn++) {
							
							switch (set) {
								
								case birth -> {
									
									if (this.bconds[(lastdigit * 13) + nn] == 2) this.bconds[(lastdigit * 13) + nn] = 0;
									
									if (nn == i) this.bconds[(lastdigit * 13) + i] = 1;
									
								}
								
								case survival -> {
									
									if (this.sconds[(lastdigit * 13) + nn] == 2) this.sconds[(lastdigit * 13) + nn] = 0;
									
									if (nn == i) this.sconds[(lastdigit * 13) + i] = 1;
									
								}
								
								case deficient -> {
									
									if (this.dconds[(lastdigit * 13) + nn] == 2) this.dconds[(lastdigit * 13) + nn] = 0;
									
									if (nn == i) this.dconds[(lastdigit * 13) + i] = 1;
									
								}
								
								case f -> {
									
									if (this.fconds[(lastdigit * 13) + nn] == 2) this.fconds[(lastdigit * 13) + nn] = 0;
									
									if (nn == i) this.fconds[(lastdigit * 13) + i] = 1;
									
								}
								
								case k -> {
									
									if (this.kconds[(lastdigit * 13) + nn] == 2) this.kconds[(lastdigit * 13) + nn] = 0;
									
									if (nn == i) this.kconds[(lastdigit * 13) + i] = 1;
									
								}
								
								case l -> {
									
									if (this.lconds[(lastdigit * 13) + nn] == 2) this.lconds[(lastdigit * 13) + nn] = 0;
									
									if (nn == i) this.lconds[(lastdigit * 13) + i] = 1;
									
								}
								
							}
							
						}
						
					}
					
				}
				
				case '-' -> negating = true;
				
				default -> {
					
					continue;
					
				}
				
			}
			
		}
		
		if ((this.statenum == 2) && (this.ruletype == Ruletype.Generations)) this.ruletype = Ruletype.INT;
		
	}
	
	void advance() {
		
		// dict is a 256-element lookup table mapping neighborhood configurations to Hensel conditions
		
		int[] dict = {0, 13, 14, 29, 13, 26, 29, 43, 14, 29, 27, 42, 28, 44, 47, 55, 14, 28, 27, 47, 29, 44, 42, 55, 30, 48, 40, 61, 48, 56, 61, 69, 13, 26, 28, 44, 31, 39, 46, 57, 29, 43, 47, 55, 46, 57, 63, 68, 28, 45, 41, 54, 46, 58, 59, 73, 48, 62, 60, 70, 64, 74, 72, 81, 14, 28, 30, 48, 28, 45, 48, 62, 27, 47, 40, 61, 41, 54, 60, 70, 27, 41, 40, 60, 47, 54, 61, 70, 40, 60, 53, 65, 60, 71, 65, 78, 29, 44, 48, 56, 46, 58, 64, 74, 42, 55, 61, 69, 59, 73, 72, 81, 47, 54, 60, 71, 63, 67, 72, 80, 61, 70, 65, 78, 72, 80, 83, 91, 13, 31, 28, 46, 26, 39, 44, 57, 28, 46, 41, 59, 45, 58, 54, 73, 29, 46, 47, 63, 43, 57, 55, 68, 48, 64, 60, 72, 62, 74, 70, 81, 26, 39, 45, 58, 39, 52, 58, 66, 44, 57, 54, 73, 58, 66, 67, 79, 44, 58, 54, 67, 57, 66, 73, 79, 56, 74, 71, 80, 74, 82, 80, 92, 29, 46, 48, 64, 44, 58, 56, 74, 47, 63, 60, 72, 54, 67, 71, 80, 42, 59, 61, 72, 55, 73, 69, 81, 61, 72, 65, 83, 70, 80, 78, 91, 43, 57, 62, 74, 57, 66, 74, 82, 55, 68, 70, 81, 73, 79, 80, 92, 55, 73, 70, 80, 68, 79, 81, 92, 69, 81, 78, 91, 81, 92, 91, 104};
	    
	    int[][] neigh = {{1, 1}, {1, 0}, {1, -1}, {0, 1}, {0, -1}, {-1, 1}, {-1, 0}, {-1, -1}};
	    
	    int[][] next = new int[200][200];
	    
	    for (int x = 0; x < 200; x++) {
	        
	        for (int y = 0; y < 200; y++) {
	            
	            int xx, yy;
	            
	            int neighs = 0;
	            
	            int binary = 0;
	            
	            int sb = 0; // For deficient rules
	            
	            for (int n = 0; n < 8; n++) {
	                
	                xx = x + neigh[n][0];
	                
	                yy = y + neigh[n][1];
	                
	                if (!(this.naive)) {
	                		
	                		xx += 200;
	                		
	                		yy += 200;
	                		
	                		xx %= 200;
	                		
	                		yy %= 200;
	                		
	                	}
	                
	                binary *= 2;
	                
	                sb *= 2;
	                
	                if ((xx >= 200) || (yy >= 200) || (xx < 0) || (yy < 0)) continue;
	                
	                if (this.ruletype == Ruletype.Generations) {
	                		
	                		if (this.world[xx][yy] == 1) {
	                			
	                			binary++; // No neighs needed here
	                			
	                		}
	                		
	                	} else if (this.ruletype == Ruletype.Deficient) {
	                		
	                		if (this.world[xx][yy] != 0) binary++;
	                			
	                		if (this.world[xx][yy] == 2) sb++;
	                		
	                	} else if (this.ruletype == Ruletype.BSFKL) {
	                		
	                		if (this.world[xx][yy] == 1) binary++;
	                			
	                		if (this.world[xx][yy] == 2) sb++;
	                		
	                	} else {
	                		
	                		if (this.world[xx][yy] != 0) {
	                			
	                			binary++;
	                			
	                			neighs++;
	                			
	                		}
	                		
	                	}
	                	
	            }
	            
	            if (this.ruletype == Ruletype.INT) {	
	  	          
	    		        if (this.naive) {
	    		        		
	    		        		if (this.color) {
	    		        			
	    		    		    		if ((this.world[x][y] == 0) && (this.bconds[dict[binary]] != 0)) {
	    		    		    			
	    		    		    			this.world[x][y] = neighs + 1;
	    		    			    			
	    		    		    			continue; // Necessary to prevent the next condition from being evaluated if this one is true
	    		    		    			
	    		    		    		}
	    		    		    		
	    		    		    		if ((this.world[x][y] != 0) && (this.sconds[dict[binary]] != 0)) {
	    		    		    			
	    		    		    			this.world[x][y] = neighs + 1;
	    		    		    			
	    		    		    			continue;
	    		    		    			
	    		    		    		}
	    		    		    		
	    		    		    		if ((this.world[x][y] != 0) && (this.sconds[dict[binary]] == 0)) this.world[x][y] = 0;
	    		        			
	    		        		} else {
	    		        			
	    		    		    		if ((this.world[x][y] == 0) && (this.bconds[dict[binary]] != 0)) {
	    		    		    			
	    		    		    			this.world[x][y] = 1;
	    		    			    			
	    		    		    			continue; // Necessary to prevent the next condition from being evaluated if this one is true
	    		    		    			
	    		    		    		}
	    		    		    		
	    		    		    		if ((this.world[x][y] != 0) && (this.sconds[dict[binary]] == 0)) this.world[x][y] = 0;
	    		        			
	    		        		}
	    		        		
	    		        	} else {
	    		        		
	    		        		if (this.color) {
	    		        			
	    		        			if ((this.world[x][y] == 0) && (this.bconds[dict[binary]] != 0)) next[x][y] = neighs + 1;
	    		        			
	    		    		    		if ((this.world[x][y] != 0) && (this.sconds[dict[binary]] != 0)) next[x][y] = neighs + 1;
	    		        			
	    		        		} else {
	    		        			
	    		        			if ((this.world[x][y] == 0) && (this.bconds[dict[binary]] != 0)) next[x][y] = 1;
	    		        			
	    		    		    		if ((this.world[x][y] != 0) && (this.sconds[dict[binary]] != 0)) next[x][y] = 1;
	    		        			
	    		        		}
	    		        		
	    		        	}
	            	
	            	}
	            	
	            	if (this.ruletype == Ruletype.Generations) {
	            		
	            		if (naive) {
	            			
	            			if ((this.world[x][y] == 0) && (this.bconds[dict[binary]] != 0)) {
	    		    		    		
	    		    		    		this.world[x][y] = 1;
	    		    			    		
	    		    		    		continue; // Necessary to prevent the next condition from being evaluated if this one is true
	    		    		    		
	    		    		    	}
	    		    		    	
	    		    		    	if ((this.world[x][y] == 1) && (this.sconds[dict[binary]] == 0)) {
	    		    		    		
	    		    		    		if (this.world[x][y] == (this.statenum - 1)) this.world[x][y] = 0;
	    		    		    		
	    		    		    		else this.world[x][y]++;
	    		    		    		
	    		    		    		continue;
	    		    		    		
	    		    		    	}
	    		    		    	
	    		    		    	if (this.world[x][y] > 1) {
	    		    		    		
	    		    		    		if (this.world[x][y] == (this.statenum - 1)) this.world[x][y] = 0;
	    		    		    		
	    		    		    		else this.world[x][y]++;
	    		    		    		
	    		    		    	}
	    		    		    	
	    		    		} else {
	    		    			
	    		    			if ((this.world[x][y] == 0) && (this.bconds[dict[binary]] != 0)) next[x][y] = 1;
	    		    			
	    		    			if ((this.world[x][y] == 1) && (this.sconds[dict[binary]] != 0)) next[x][y] = 1;
	    		    			
	    		    			if ((this.world[x][y] == 1) && (this.sconds[dict[binary]] == 0)) {
	    		    		    		
	    		    		    		if (this.world[x][y] == (this.statenum - 1)) next[x][y] = 0;
	    		    		    		
	    		    		    		else next[x][y] = world[x][y] + 1;
	    		    		    		
	    		    		    	}
	    		    		    	
	    		    			if (this.world[x][y] > 1) {
	    		    				
	    		    		    		if (this.world[x][y] == (this.statenum - 1)) next[x][y] = 0;
	    		    		    		
	    		    		    		else next[x][y] = world[x][y] + 1;
	    		    		    		
	    		    		    	}
	    		    		    	
	    		    		}
	            		
	            	}
	            	
	            	if (this.ruletype == Ruletype.Deficient) {
	            		
	            		if (naive) {
	            			
	            			if ((this.world[x][y] == 0) && (this.bconds[dict[binary]] != 0) && (this.dconds[dict[binary]] == 0)) {
	            				
	            				this.world[x][y] = 1;
	            				
	            				continue;
	            				
	            			}
	            			
	            			if ((this.world[x][y] == 0) && (this.bconds[dict[binary]] != 0) && (this.dconds[dict[binary]] != 0) && (sb == 0)) {
	            				
	            				this.world[x][y] = 2;
	            				
	            				continue;
	            				
	            			}
	            			
	            			if ((this.world[x][y] == 2) && (this.sconds[dict[binary]] != 0)) {
	            				
	            				this.world[x][y] = 1;
	            				
	            				continue;
	            				
	            			}
	            			
	            			if ((this.world[x][y] == 2) && (this.sconds[dict[binary]] == 0)) {
	            				
	            				this.world[x][y] = 0;
	            				
	            				continue;
	            				
	            			}
	            			
	            			if ((this.world[x][y] == 1) && (this.sconds[dict[binary]] == 0)) this.world[x][y] = 0;
	            			
	            		} else {
	            			
	            			if ((this.world[x][y] == 0) && (this.bconds[dict[binary]] != 0) && (this.dconds[dict[binary]] == 0)) next[x][y] = 1;
	            			
	            			if ((this.world[x][y] == 0) && (this.bconds[dict[binary]] != 0) && (this.dconds[dict[binary]] != 0) && (sb == 0)) next[x][y] = 2;
	            			
	            			if ((this.world[x][y] == 2) && (this.sconds[dict[binary]] != 0)) next[x][y] = 1;
	            			
	            			if ((this.world[x][y] == 1) && (this.sconds[dict[binary]] != 0)) next[x][y] = 1;
	            			
	            		}
	            		
	            	}
	            	
	            	if (this.ruletype == Ruletype.BSFKL) {
	            		
	            		if (naive) {
	            			
	            			if ((this.world[x][y] == 0) && (this.bconds[dict[binary]] != 0) && (this.fconds[dict[sb]] != 0)) {
	            				
	            				this.world[x][y] = 1;
	            				
	            				continue;
	            				
	            			}
	            			
	            			if ((this.world[x][y] == 1) && (this.kconds[dict[sb]] != 0)) {
	            				
	            				this.world[x][y] = 0;
	            				
	            				continue;
	            				
	            			} else if ((this.world[x][y] == 1) && (this.sconds[dict[binary]] != 0)) {
	            				
	            				continue;
	            				
	            			} else if (this.world[x][y] == 1) {
	            				
	            				this.world[x][y] = 2;
	            				
	            				continue;
	            				
	            			}
	            			
	            			if ((this.world[x][y] == 2) && (this.lconds[dict[binary]] != 0)) this.world[x][y] = 0;
	            			
	            		} else {
	            			
	            			if ((this.world[x][y] == 0) && (this.bconds[dict[binary]] != 0) && (this.fconds[dict[sb]] != 0)) next[x][y] = 1;
	            			
	            			if ((this.world[x][y] == 1) && (this.kconds[dict[sb]] != 0)) next[x][y] = 0;
	            			
	            			else if ((this.world[x][y] == 1) && (this.sconds[dict[binary]] != 0)) next[x][y] = 1;
	            			
	            			else if (this.world[x][y] == 1) next[x][y] = 2;
	            			
	            			if ((this.world[x][y] == 2) && (this.lconds[dict[binary]] != 0)) next[x][y] = 0;
	            			
	            			else if (this.world[x][y] == 2) next[x][y] = 2;
	            			
	            		}
	            		
	            	}
	            	
	        }
	        
	    }
	    
	    if (!(this.naive)) this.world = next;
	    
	}
	
	Color avg(Color c1, Color c2) {
		
		int r = (c1.getRed() + c2.getRed()) / 2;
		
		int g = (c1.getGreen() + c2.getGreen()) / 2;
		
		int b = (c1.getBlue() + c2.getBlue()) / 2;
		
		return new Color(r, g, b);
		
	}
	
	Color gencolor(int n) {
		
		int r, g, b;
		
		int[] finalcolor = new int[3];
		
		if (n > 0) {
			
			r = (this.statenum - 1 - n) * (255 / (this.statenum - 2));
			
			g = r;
			
			b = 255;
			
			return new Color(r, g, b);
			
		}
		
		return Color.black;
		
	}
		
	String sort(String string) { // Borrowed from GlidINT
		
		String sorted = "";
		
		for (char n = 'a'; n <= 'z'; n++) {
			
			for (int c = 0; c < string.length(); c++) {
				
				if (string.charAt(c) == n) sorted += n;
				
			}
			
		}
		
		return sorted;
		
	}
	
	String writerule() { // Borrowed from GlidINT, adapted
		
		String rulestring = "";
		
		String letters = "cekainyqjrtwz";
		
		String set;
		
		String sorted;
		
		int c;
		
		int[] limits = {1, 2, 6, 10, 13, 10, 6, 2, 1};
		
		if (this.naive) rulestring += "N";
		
		rulestring += "B";
		
		for (int n = 0; n <= 8; n++) {
			
			set = "";
			
			sorted = "";
			
			c = 0;
			
			for (int nn = 0; nn < limits[n]; nn++) {
				
				if (this.bconds[(13 * n) + nn] != 0) {
					
					c++;
					
				}
				
			}
			
			if (c == 0) {
				
				continue;
				
			} else if (c == limits[n]) {
				
				rulestring += (char) (n + '0');
				
			} else if (c <= Math.ceilDiv(limits[n], 2)) {
				
				rulestring += (char) (n + '0');
				
				for (int nn = 0; nn < limits[n]; nn++) {
					
					if (this.bconds[(13 * n) + nn] != 0) {
						
						set += letters.charAt(nn);
						
					}
					
				}
				
				sorted = sort(set);
				
				for (int nnn = 0; nnn < sorted.length(); nnn++) {
					
					rulestring += sorted.charAt(nnn);
					
				}
				
			} else if (c < limits[n]) {
				
				rulestring += (char) (n + '0');
				
				rulestring += "-";
				
				for (int nn = 0; nn < limits[n]; nn++) {
					
					if (this.bconds[(13 * n) + nn] == 0) {
						
						set += letters.charAt(nn);
						
					}
					
				}
				
				sorted = sort(set);
				
				for (int nnn = 0; nnn < sorted.length(); nnn++) {
					
					rulestring += sorted.charAt(nnn);
					
				}
				
			}
			
		}
		
		rulestring += "/S";
		
		for (int n = 0; n <= 8; n++) {
			
			set = "";
			
			sorted = "";
			
			c = 0;
			
			for (int nn = 0; nn < limits[n]; nn++) {
				
				if (this.sconds[(13 * n) + nn] != 0) {
					
					c++;
					
				}
				
			}
			
			// System.out.println(n + " " + c);
			
			if (c == 0) {
				
				continue;
				
			} else if (c == limits[n]) {
				
				rulestring += (char) (n + '0');
				
			} else if (c <= Math.ceilDiv(limits[n], 2)) {
				
				rulestring += (char) (n + '0');
				
				for (int nn = 0; nn < limits[n]; nn++) {
					
					if (this.sconds[(13 * n) + nn] != 0) {
						
						set += letters.charAt(nn);
						
					}
					
				}
				
				sorted = sort(set);
				
				// System.out.println(sorted);
				
				for (int nnn = 0; nnn < sorted.length(); nnn++) {
					
					rulestring += sorted.charAt(nnn);
					
				}
				
			} else if (c < limits[n]) {
				
				rulestring += (char) (n + '0');
				
				rulestring += "-";
				
				for (int nn = 0; nn < limits[n]; nn++) {
					
					if (this.sconds[(13 * n) + nn] == 0) {
						
						set += letters.charAt(nn);
						
					}
					
				}
				
				sorted = sort(set);
				
				for (int nnn = 0; nnn < set.length(); nnn++) {
					
					rulestring += sorted.charAt(nnn);
					
				}
				
			}
			
		}
		
		if ((this.ruletype == Ruletype.Generations) && (this.statenum > 2)) {
			
			rulestring += "/G";
			
			rulestring += this.statenum;
			
		}
		
		if (this.ruletype == Ruletype.Deficient) {
			
			rulestring += "/D";
			
			for (int n = 0; n <= 8; n++) {
				
				set = "";
				
				sorted = "";
				
				c = 0;
				
				for (int nn = 0; nn < limits[n]; nn++) {
					
					if (this.dconds[(13 * n) + nn] != 0) {
						
						c++;
						
					}
					
				}
				
				// System.out.println(n + " " + c);
				
				if (c == 0) {
					
					continue;
					
				} else if (c == limits[n]) {
					
					rulestring += (char) (n + '0');
					
				} else if (c <= Math.ceilDiv(limits[n], 2)) {
					
					rulestring += (char) (n + '0');
					
					for (int nn = 0; nn < limits[n]; nn++) {
						
						if (this.dconds[(13 * n) + nn] != 0) {
							
							set += letters.charAt(nn);
							
						}
						
					}
					
					sorted = sort(set);
					
					// System.out.println(sorted);
					
					for (int nnn = 0; nnn < sorted.length(); nnn++) {
						
						rulestring += sorted.charAt(nnn);
						
					}
					
				} else if (c < limits[n]) {
					
					rulestring += (char) (n + '0');
					
					rulestring += "-";
					
					for (int nn = 0; nn < limits[n]; nn++) {
						
						if (this.dconds[(13 * n) + nn] == 0) {
							
							set += letters.charAt(nn);
							
						}
						
					}
					
					sorted = sort(set);
					
					for (int nnn = 0; nnn < set.length(); nnn++) {
						
						rulestring += sorted.charAt(nnn);
						
					}
					
				}
				
			}
			
		}
		
		if (this.ruletype == Ruletype.BSFKL) {
			
			rulestring += "/F";
			
			for (int n = 0; n <= 8; n++) {
				
				set = "";
				
				sorted = "";
				
				c = 0;
				
				for (int nn = 0; nn < limits[n]; nn++) {
					
					if (this.fconds[(13 * n) + nn] != 0) {
						
						c++;
						
					}
					
				}
				
				// System.out.println(n + " " + c);
				
				if (c == 0) {
					
					continue;
					
				} else if (c == limits[n]) {
					
					rulestring += (char) (n + '0');
					
				} else if (c <= Math.ceilDiv(limits[n], 2)) {
					
					rulestring += (char) (n + '0');
					
					for (int nn = 0; nn < limits[n]; nn++) {
						
						if (this.fconds[(13 * n) + nn] != 0) {
							
							set += letters.charAt(nn);
							
						}
						
					}
					
					sorted = sort(set);
					
					// System.out.println(sorted);
					
					for (int nnn = 0; nnn < sorted.length(); nnn++) {
						
						rulestring += sorted.charAt(nnn);
						
					}
					
				} else if (c < limits[n]) {
					
					rulestring += (char) (n + '0');
					
					rulestring += "-";
					
					for (int nn = 0; nn < limits[n]; nn++) {
						
						if (this.fconds[(13 * n) + nn] == 0) {
							
							set += letters.charAt(nn);
							
						}
						
					}
					
					sorted = sort(set);
					
					for (int nnn = 0; nnn < set.length(); nnn++) {
						
						rulestring += sorted.charAt(nnn);
						
					}
					
				}
				
			}
			
			rulestring += "/K";
			
			for (int n = 0; n <= 8; n++) {
				
				set = "";
				
				sorted = "";
				
				c = 0;
				
				for (int nn = 0; nn < limits[n]; nn++) {
					
					if (this.kconds[(13 * n) + nn] != 0) {
						
						c++;
						
					}
					
				}
				
				// System.out.println(n + " " + c);
				
				if (c == 0) {
					
					continue;
					
				} else if (c == limits[n]) {
					
					rulestring += (char) (n + '0');
					
				} else if (c <= Math.ceilDiv(limits[n], 2)) {
					
					rulestring += (char) (n + '0');
					
					for (int nn = 0; nn < limits[n]; nn++) {
						
						if (this.kconds[(13 * n) + nn] != 0) {
							
							set += letters.charAt(nn);
							
						}
						
					}
					
					sorted = sort(set);
					
					// System.out.println(sorted);
					
					for (int nnn = 0; nnn < sorted.length(); nnn++) {
						
						rulestring += sorted.charAt(nnn);
						
					}
					
				} else if (c < limits[n]) {
					
					rulestring += (char) (n + '0');
					
					rulestring += "-";
					
					for (int nn = 0; nn < limits[n]; nn++) {
						
						if (this.kconds[(13 * n) + nn] == 0) {
							
							set += letters.charAt(nn);
							
						}
						
					}
					
					sorted = sort(set);
					
					for (int nnn = 0; nnn < set.length(); nnn++) {
						
						rulestring += sorted.charAt(nnn);
						
					}
					
				}
				
			}
			
			rulestring += "/L";
			
			for (int n = 0; n <= 8; n++) {
				
				set = "";
				
				sorted = "";
				
				c = 0;
				
				for (int nn = 0; nn < limits[n]; nn++) {
					
					if (this.lconds[(13 * n) + nn] != 0) {
						
						c++;
						
					}
					
				}
				
				// System.out.println(n + " " + c);
				
				if (c == 0) {
					
					continue;
					
				} else if (c == limits[n]) {
					
					rulestring += (char) (n + '0');
					
				} else if (c <= Math.ceilDiv(limits[n], 2)) {
					
					rulestring += (char) (n + '0');
					
					for (int nn = 0; nn < limits[n]; nn++) {
						
						if (this.lconds[(13 * n) + nn] != 0) {
							
							set += letters.charAt(nn);
							
						}
						
					}
					
					sorted = sort(set);
					
					// System.out.println(sorted);
					
					for (int nnn = 0; nnn < sorted.length(); nnn++) {
						
						rulestring += sorted.charAt(nnn);
						
					}
					
				} else if (c < limits[n]) {
					
					rulestring += (char) (n + '0');
					
					rulestring += "-";
					
					for (int nn = 0; nn < limits[n]; nn++) {
						
						if (this.lconds[(13 * n) + nn] == 0) {
							
							set += letters.charAt(nn);
							
						}
						
					}
					
					sorted = sort(set);
					
					for (int nnn = 0; nnn < set.length(); nnn++) {
						
						rulestring += sorted.charAt(nnn);
						
					}
					
				}
				
			}
			
		}
		
		return rulestring;
		
	}
	
	@Override
	
	public void update(Graphics g) {
		
		paint(g);
		
	}
	
	@Override
	
	public void paint(Graphics g) {
		
		switch (mode) {
			
			case Running:
				
				int minx = (this.region[0][0] < this.region[1][0]) ? this.region[0][0] : this.region[1][0];
				
				int maxx = (this.region[0][0] > this.region[1][0]) ? this.region[0][0] : this.region[1][0];
				
				int miny = (this.region[0][1] < this.region[1][1]) ? this.region[0][1] : this.region[1][1];
				
				int maxy = (this.region[0][1] > this.region[1][1]) ? this.region[0][1] : this.region[1][1];
				
				if (this.color) {
					
					Color[] colors = {Color.black, Color.white, Color.gray, Color.red, Color.green, Color.blue, Color.orange, new Color(128, 000, 255), Color.yellow, Color.magenta};
					
			    		for (int x = 0; x < 200; x++) {
						
						for (int y = 0; y < 200; y++) {
							
							if ((minx <= x) && (x < maxx) && (miny <= y) && (y < maxy)) {
								
								Color current = colors[this.world[x][y]];
								
								Color overlay = avg(new Color(0, 255, 0), current);
								
								g.setColor(overlay);
								
							} else {
								
								g.setColor(colors[this.world[x][y]]);
								
							}
							
							g.fillRect(4 * y, 4 * x, 4, 4);
							
						}
						
					}
					
					g.setColor(avg(new Color(255, 0, 0), colors[this.world[this.cursor[0]][this.cursor[1]]]));
					
					g.fillRect(4 * this.cursor[1], 4 * this.cursor[0], 4, 4);
					
				} else {
					
					if (this.ruletype == Ruletype.INT) {
						
						Color[] colors = {Color.black, Color.white};
						
				    		for (int x = 0; x < 200; x++) {
							
							for (int y = 0; y < 200; y++) {
								
								if ((minx <= x) && (x < maxx) && (miny <= y) && (y < maxy)) {
									
									Color current = colors[this.world[x][y]];
									
									Color overlay = avg(new Color(0, 255, 0), current);
									
									g.setColor(overlay);
									
								} else {
									
									g.setColor(colors[this.world[x][y]]);
									
								}
								
								g.fillRect(4 * y, 4 * x, 4, 4);
								
							}
							
						}
						
						g.setColor(avg(new Color(255, 0, 0), colors[this.world[this.cursor[0]][this.cursor[1]]]));
						
						g.fillRect(4 * this.cursor[1], 4 * this.cursor[0], 4, 4);
						
					}
					
				}
				
				if (this.ruletype == Ruletype.Generations) {
					
				    	for (int x = 0; x < 200; x++) {
						
						for (int y = 0; y < 200; y++) {
							
							if ((minx <= x) && (x < maxx) && (miny <= y) && (y < maxy)) {
								
								Color current = gencolor(this.world[x][y]);
								
								Color overlay = avg(new Color(0, 255, 0), current);
								
								g.setColor(overlay);
								
							} else {
								
								g.setColor(gencolor(this.world[x][y]));
								
							}
							
							g.fillRect(4 * y, 4 * x, 4, 4);
							
						}
						
					}
					
					g.setColor(avg(new Color(255, 0, 0), gencolor(this.world[this.cursor[0]][this.cursor[1]])));
					
					g.fillRect(4 * this.cursor[1], 4 * this.cursor[0], 4, 4);
					
				}
				
				if (this.ruletype == Ruletype.Deficient) {
					
					Color[] colors = {Color.black, Color.white, Color.yellow};
					
				    	for (int x = 0; x < 200; x++) {
						
						for (int y = 0; y < 200; y++) {
							
							if ((minx <= x) && (x < maxx) && (miny <= y) && (y < maxy)) {
								
								Color current = colors[this.world[x][y]];
								
								Color overlay = avg(new Color(0, 255, 0), current);
								
								g.setColor(overlay);
								
							} else {
								
								g.setColor(colors[this.world[x][y]]);
								
							}
							
							g.fillRect(4 * y, 4 * x, 4, 4);
							
						}
						
					}
					
					g.setColor(avg(new Color(255, 0, 0), colors[this.world[this.cursor[0]][this.cursor[1]]]));
					
					g.fillRect(4 * this.cursor[1], 4 * this.cursor[0], 4, 4);
					
				}
				
				if (this.ruletype == Ruletype.BSFKL) {
					
					Color[] colors = {Color.black, new Color(0, 192, 255), Color.red};
					
				    	for (int x = 0; x < 200; x++) {
						
						for (int y = 0; y < 200; y++) {
							
							if ((minx <= x) && (x < maxx) && (miny <= y) && (y < maxy)) {
								
								Color current = colors[this.world[x][y]];
								
								Color overlay = avg(new Color(0, 255, 0), current);
								
								g.setColor(overlay);
								
							} else {
								
								g.setColor(colors[this.world[x][y]]);
								
							}
							
							g.fillRect(4 * y, 4 * x, 4, 4);
							
						}
						
					}
					
					g.setColor(avg(new Color(255, 0, 0), colors[this.world[this.cursor[0]][this.cursor[1]]]));
					
					g.fillRect(4 * this.cursor[1], 4 * this.cursor[0], 4, 4);
					
				}
				
				break;
				
			case SettingRule:
				
				g.setColor(Color.black);
				
				g.fillRect(0, 0, 800, 800);
				
				g.setColor(Color.white);
				
				g.drawString(this.rulestring, 400, 400);
				
				break;
				
		}
		
	}
		
}

class NaiViewer {
	
	public static void main(String[] args) {
		
		Funcs funcs = new Funcs();
		
		funcs.setBackground(Color.black);
		
		funcs.setForeground(Color.white);
		
		funcs.setSize(new Dimension(800, 800));
		
		funcs.setTitle("NaiViewer (B3/S23)");
		
		funcs.setVisible(true);
		
		funcs.rparse("B3/S23");
		
	}
	
}
Feel free to report any bugs or discoveries here.

Edit (September 13th, 2026, 10:33 AM): The updated version is now on Github by the name “NaiViewer-java”.
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