# Making a Complex Grid

Discussion in 'Scripting' started by EternalAmbiguity, Jan 16, 2017.

1. ### EternalAmbiguity

Joined:
Dec 27, 2014
Posts:
2,526
I'm in school getting a master's degree, and part of my research uses cellular automata. The current programs I use were all made by a few guys twenty years ago (literally), so they're hard to work with and have some issues (that won't ever be fixed, because the guy who made the program isn't coding anymore).

I'm trying to make a cellular automaton in Unity. I need to be able:

i. To set up time to progress in iterations, rather than continuously,

1. to make a grid of variable dimensions

2. with three shapes: a rectangle (all sides closed), a cylinder (so something at one side would jump to the opposite side, but top/bottom or left/right would be closed), and a torus (all sides open like the cylinder),

3. with each point or node on the grid holding an "agent" able to be one of several "states" (represented by colors), with the change between states per iteration defined by a probability (p=0.25 for A to B, p=0.25 for A to C, p=0.50 for A to A

4. with each agent on the grid able to detect the state of the other points around it (Moore or von Neumann neighborhood), and able to update its probability values for state change based on those other cells/agents around it,

5. with each agent able to move to different spots on the grid based on both a random jump system, and some preferential system (such as a gravitational field),

6. with each agent able to join or break with others around it (based on some probability).

I think that's everything.

I. I'm not too sure about how I'll set up time in iterations, but I don't think it will be super hard.

1. I've had some trouble finding a decent grid system. This looks like it might be useful.

2. Setting up the shapes is probably easy, but I don't have any idea how to go about it.

3. This seems like it will be very simple to do, though I have no experience with using probabilities in Unity and its RNG.

4. Absolutely no clue how this will be done. None whatsoever.

5. Same as above. Interaction between different parts of a grid seems very hard.

6. Probably not too hard, as long as one can do 4.

I realize I'll probably have to do most of this myself, but can anyone help me out with any of this at all? I would really appreciate anything anyone can offer.

2. ### GroZZleR

Joined:
Feb 1, 2015
Posts:
2,205
Do you have not a lot of programming experience? This seems like a fairly straight forward list of problems to solve.

Grids can be represented with 1D arrays quite easily:
Code (csharp):
1.
2. Cell[] cells = new Cell[width * height];
3.
4. void SetCell(int x, int y, Cell value)
5. {
6.     cells[y * width + x] = value;
7. }
8.
9. Cell GetCell(int x, int y)
10. {
11.   return cells[y * width + x];
12. }
13.
So that solves problems 4-6.

Problem 1 can be solved via a timer or coroutine.

Code (csharp):
1.
2. float time = 0.0;
3. float timeStep = 0.2f;
4.
5. void Update()
6. {
7.    time += Time.deltaTime;
8.
9.    while(time >= timeStep)
10.    {
11.      // do iteration logic
12.
13.     time -= timeStep;
14.    }
15. }
16.
Problem 2 is solvable with Unity's default primitives (GameObject -> 3D Object -> choose a shape).

Problem 3 uses material.color and Random.Range().

3. ### EternalAmbiguity

Joined:
Dec 27, 2014
Posts:
2,526
I don't have a whole lot of programming experience, no.

Thanks for the help. This seems like it will help a lot. However, for problem 2--it isn't literally making a rectangle or a torus. It's making a representation of one with the grid. So for a rectangle, the cells or agents or whatever can move anywhere in the square, but cannot go through any of the edges. This means that if I have a bunch of red cells and one green one, the green one can move to the edge but cannot move any further.

Then, for a cylinder, they can pass through one barrier to the opposite one. So the green cell could go all the way to the right edge of the boundary, then cross it to wind up on the left side. However, the other "set" of boundaries are blocked, so the green cell could not go through the top or bottom edges.

Then for a torus, all edges are accessible, and lead to the opposite edge.

It's still a regular grid, it's not some 3D object. But the cells or whatever can move in different ways.

Again, thanks for the help. I really should have seen that about 4-6 myself.

4. ### takatok

Joined:
Aug 18, 2016
Posts:
1,496
I would suggest separating the actual automata and its data from the graphics implementation. Then you could have it as a backend and implement whatever graphics you needed.

Here is a simple data class for an Array the implements Moore and VonNeumann neighborhoods as well as bounded rectangle, 2 types of cylinders and a torus. I wrote a simple Conway's life implementation in the OneTick method. I put check on the neighborhood and wrote the standard rules for a Moore, as well as made up rules for a VonNeumann one to show you could. This script would just put put in your project folder not attached to a game object.

A GO that was handling graphics would have this class as a member and just run the OneTick method each time it wanted to update the CA. You would just implement whatever rules you wanted int the OneTick method.

Code (CSharp):
1. using UnityEngine;
2. using System.Collections;
3. using System.Collections.Generic;
4.
5. public class CAGrid  {
6.
7.     private NeighborHood nHood;
8.     private GridType gType;
9.     private int width;
10.     private int height;
11.     private int[] cells;
12.     private int[] backup;
13.     List<CellOffset> neighbors;
14.
15.     public CAGrid()
16.         : this(10,10,NeighborHood.Moore,GridType.Rectangle)
17.     {}
18.
19.     public CAGrid(int width, int height,NeighborHood hood, GridType type)
20.     {
21.         nHood = hood;
22.         gType = type;
23.         cells = new int[width * height];
24.         backup = new int[width * height];
25.         this.width = width;
26.         this.height = height;
27.         InitNeighbors();
28.
29.     }
30.
31.     public void OneTick()
32.     {
33.         // Code here to run your CA
34.         for (int i = 0; i < width; ++i)
35.             for (int j = 0; j < height; ++j)
36.                 SetBackupCell(i, j, 0);
37.
38.         for (int i = 0; i < width; ++i)
39.         {
40.             for (int j = 0; j < height; ++j)
41.             {
42.                 List<int> neighbors = GetNeighbors(i, j);
43.                 int sum = 0;
44.                 foreach (int oneNeighbor in neighbors)
45.                     sum += oneNeighbor;
46.                 if (nHood == NeighborHood.VonNeumann)
47.                 {
48.                     if (sum < 2 || sum > 3)
49.                         SetBackupCell(i, j, 0);
50.                     else
51.                         SetBackupCell(i, j, 1);
52.                 }
53.                 else if (nHood == NeighborHood.Moore)
54.                 {
55.                     if (sum < 1 || sum > 2)
56.                         SetBackupCell(i, j, 0);
57.                     else
58.                         SetBackupCell(i, j, 1);
59.                 }
60.             }
61.         }
62.         backup.CopyTo(cells, 0);
63.
64.     }
65.
66.     void SetCell(int x, int y, int value)
67.     {
68.         cells[y * width + x] = value;
69.     }
70.
71.     int GetCell(int x, int y)
72.     {
73.         return cells[y * width + x];
74.     }
75.
76.     void SetBackupCell(int x, int y, int value)
77.     {
78.         backup[y * width + x] = value;
79.     }
80.
81.     List<int> GetNeighbors(int x, int y)
82.     {
83.         List<int> neighborValues = new List<int>();
84.         foreach(CellOffset offset in neighbors)
85.         {
86.             int currentX = x + offset.x;
87.             int currentY = y + offset.y;
88.             if (ValidSpot(ref currentX, ref currentY))
90.         }
91.         return neighborValues;
92.     }
93.
94.     bool ValidSpot(ref int x, ref int y)
95.     {
96.         switch (gType)
97.         {
98.             case GridType.Rectangle:
99.                 if (x < 0 || x >= width || y < 0 || y >= height)
100.                     return false;
101.                 return true;
102.             case GridType.CylinderWidth:
103.                 if (y < 0 || y >= height)
104.                     return false;
105.                 if (x < 0)
106.                     x = width + x;
107.                 if (x >= width)
108.                     x -= width;
109.                 return true;
110.             case GridType.CylinderHeight:
111.                 if (x < 0 || x >= width)
112.                     return false;
113.                 if (y < 0)
114.                     y = height + y;
115.                 if (y >= height)
116.                     y -= height;
117.                 return true;
118.             case GridType.Torus:
119.                 if (x < 0)
120.                     x = width + x;
121.                 if (x >= width)
122.                     x -= width;
123.                 if (y < 0)
124.                     y = height + y;
125.                 if (y >= height)
126.                     y -= height;
127.                 return true;
128.             default:
129.                 return false;
130.         }
131.     }
132.
133.     void InitNeighbors()
134.     {
135.         switch (nHood)
136.         {
137.             case NeighborHood.VonNeumann:
138.                 neighbors = new List<CellOffset>();
143.                 break;
144.             case NeighborHood.Moore:
145.                 neighbors = new List<CellOffset>();
154.                 break;
155.         }
156.     }
157.
158.
159. }
160.
161. public struct CellOffset
162. {
163.     public int x;
164.     public int y;
165.
166.     public CellOffset(int x, int y)
167.     {
168.         this.x = x;
169.         this.y = y;
170.     }
171. }
172. public enum NeighborHood
173. {
174.     Moore,
175.     VonNeumann
176. }
177.
178. public enum GridType
179. {
180.     Rectangle,
181.     CylinderWidth,
182.     CylinderHeight,
183.     Torus
184. }
185.
Hopefully its pretty straightforward in what I did.. reply here or privately to me if you need help with any of it.

One thing it doesn't implement is an infinite grid. You could easily to do that by adding another boolean variable, and methods for expanding the array. But I would suggest that for something later after you get everything you want working right.

5. ### EternalAmbiguity

Joined:
Dec 27, 2014
Posts:
2,526
Hmm. I'm guessing this is probably faster than my 2000+ line script that I've come up with (that isn't complete yet).

Thanks a bunch. I'll have to take a look at this. There are a few differences (the CA we use are probabilistic, we use more cell types than "on" or "off") but I imagine that would be simple to implement once I understand this.

Where I have the most trouble is connecting the visuals with the backend in something like this. With that in mind, if I wanted to represent what you have here visually, what exactly would I do? Once I know that, I can probably mess around with it to get what I need.

6. ### takatok

Joined:
Aug 18, 2016
Posts:
1,496
Well since the grids in my class are ints you can have as many states as you want. (well up to 2^32 anyway .
As far as graphics there are lots of options. To me the easiest would be to make a 2D project and just create a lot of sprites that are just solid blocks of color. Then you can iterate through them and set their color to whatever you wanted.
You would create one Script that is a Monobehaviour attach it to a Empty GameObject. Have the the your CA class as a variable in this script, as well as List<SpriteRenderer> that holds all your sprites your created. You just call the CA class to get the value of each cell, and then change the color of that spriteRenderer in your list.

Heading out for a bit I'll post some code later on creating an array of Sprites and changing their color later if you need it.

7. ### EternalAmbiguity

Joined:
Dec 27, 2014
Posts:
2,526
I would like that. Thanks a bunch.

The way I'm currently creating a grid is with this:

Code (csharp):
1. gridWidth = int.Parse(GameObject.Find("Horigrid").GetComponent<Text>().text);
2.   gridHeight = int.Parse(GameObject.Find("Vertgrid").GetComponent<Text>().text);
3.   float xOffset = 0.0f, yOffset = 0.0f;
4.   for (int xTilesCreated = 0; xTilesCreated < gridWidth; xTilesCreated += 1)
5.   {
6.   for (int yTilesCreated = 0; yTilesCreated < gridHeight; yTilesCreated += 1)
7.   {
8.   Vector3 position = new Vector3(transform.position.x + xOffset, transform.position.y + yOffset, transform.position.z);
9.
10.   GameObject tile = Instantiate(tilePrefab, position, Quaternion.identity) as GameObject;
11.   yOffset += transform.localScale.y + tileDistance;
12.   tile.name = ("Cell" + i++);
13.   tile.tag = ("Cells");
14.   }
15.   xOffset += transform.localScale.x + tileDistance;
16.   yOffset = 0.0f;
17.   }
18.   tilePrefab.GetComponent<MeshRenderer>().enabled = false;
19.   tiles = GameObject.FindGameObjectsWithTag("Cells");
20.   foreach (GameObject tile in tiles)
21.   {
22.   tile.GetComponent<MeshRenderer>().enabled = true;
23.   }
That seems to work fine (though I still needed to recenter and scale it to fit the screen), and I was moving on to figure out how to do the color changes. That would have worked like so:

Code (csharp):
1. int newColor1 = 0;
2. Color color1 = Color.clear;
3. if (newColor1 == 1) color1 = Color.black;
4.   if (newColor1 == 2) color1 = Color.blue;
5.   if (newColor1 == 3) color1 = Color.cyan;
6.   if (newColor1 == 4) color1 = Color.gray;
7.   if (newColor1 == 5) color1 = Color.green;
8.   if (newColor1 == 6) color1 = Color.magenta;
9.   if (newColor1 == 7) color1 = Color.red;
10.   if (newColor1 == 8) color1 = Color.white;
11.   if (newColor1 == 9) color1 = Color.yellow;
12. newColor1 = GameObject.Find("NMP1.2").transform.Find("ColorDropdown").GetComponent<Dropdown>().value;
13. while (time >= timeStep)
14.   {
15.   float result = Random.Range(0f, 100f);
16.   // do iteration logic
17.   foreach (GameObject tile in tiles)
18.   {
19.   int cellNumber = int.Parse(GameObject.FindGameObjectWithTag("numbercelltype").GetComponent<Text>().text);
20.   if (cellNumber == 2)
21.   {
22.   if (cellType == 1)
23.   {
24.   GameObject basePage = GameObject.Find("NMP1." + cellType);
25.   GameObject prob = basePage.transform.Find("1stProbContent").gameObject;
26.   float prob1 = float.Parse(prob.transform.Find("ProbInputField1.1").Find("ProbTransValue").GetComponent<Text>().text);
27.   if (result <= prob1)
28.   {
29.   tile.GetComponent<MeshRenderer>().material.color = color2;
30.   cellType = 2;
31.   }
32.   }
33.   if (cellType == 2)
34.   {
35.   GameObject basePage = GameObject.Find("NMP1." + cellType);
36.   GameObject prob = basePage.transform.Find("1stProbContent").gameObject;
37.   float prob1 = float.Parse(prob.transform.Find("ProbInputField1.1").Find("ProbTransValue").GetComponent<Text>().text);
38.   if (result <= prob1)
39.   {
40.   tile.GetComponent<MeshRenderer>().material.color = color1;
41.   cellType = 1;
42.   }
43.   }
44.   }
That's all part of the same script. Thing is, there are nine colors, so I have to call each one. Additionally, because there's no easy way to convert a string or int into a color, I had to manually assign the colors to cell types. Then I had the probabilistic part.

I hadn't yet tried that out.

Last edited: Jan 18, 2017
8. ### EternalAmbiguity

Joined:
Dec 27, 2014
Posts:
2,526
Alright, I'm getting it to work on my own here. I've got a functioning probabilistic CA at the very least. It's slow going, and the majority of my difficulty is in the graphical representation, but it is working.

If you want to still post something, feel free, but I think I know what to do for the time being. Not too sure about 5 and 6 yet, but that will come in time..

9. ### takatok

Joined:
Aug 18, 2016
Posts:
1,496
Was sick the last 2 days. Here is some code that creates a grid of spirtes with a SetColor public method.
Code (CSharp):
1. using UnityEngine;
2. using System.Collections;
3. using System.Collections.Generic;
4.
5. public class GenericTest : MonoBehaviour
6. {
7.
8.     public int width;
9.     public int height;
10.     public Color[] stateColors;
11.     public Material mat;
12.
13.     List<SpriteRenderer> cells;
14.
15.
16.     void Start()
17.     {
18.
19.         cells = new List<SpriteRenderer>();
20.         float screenHeight = 2 * Camera.main.orthographicSize;
21.         float screenWidth = screenHeight * Camera.main.aspect;
22.
23.         // figure out how width/height of one cell
24.         float heightOffset = screenHeight / height;
25.         float widthOffset = screenWidth / width;
26.
27.         // the 0,0 point is in the middle of the screen
28.         // so we start at -1/2 width, -1/2 height
29.         float xStart = -screenWidth / 2f;
30.         float yStart = -screenHeight / 2f;
31.
32.
33.         for (int j=0;j<height;++j)
34.         {
35.             float yOffset = yStart + j * heightOffset;
36.
37.             for (int i=0;i<width;++i)
38.             {
39.                 float xOffset = xStart + i * widthOffset;
40.                 SpriteRenderer rend = MakeSprite(widthOffset, heightOffset);
41.                 rend.gameObject.transform.position = new Vector3(xOffset, yOffset, 0);
43.             }
44.         }
45.     }
46.
47.     public void SetColor(int i,int j, int state)
48.     {
49.         cells[j * width + i].material.color = stateColors[state]
50.     }
51.
52.     SpriteRenderer MakeSprite(float widthOffset, float heightOffset)
53.     {
54.         int texWidth = (int)(widthOffset * 100f);
55.         int texHeight = (int)(heightOffset * 100f);
56.
57.
58.         Texture2D tex = new Texture2D(texWidth, texHeight);
59.         Sprite sprite = Sprite.Create(tex, new Rect(Vector2.zero, new Vector2(texWidth,texHeight)), Vector2.zero);
60.         GameObject oneSprite = new GameObject();
62.         rend.sprite = sprite;
63.         Material spriteMat = new Material(mat);
64.         rend.material = spriteMat;
65.
66.         return rend;
67.     }
68. }
69.
Some things to know about 2D orthographic space in Unity. When you have an orthographic camera the main thing to set is the orthographic size. It defaults to 5. Unity will then setup a space that is 2*size tall (so default its 10 units tall). Then depending on your aspect ratio it will set the x-axis. So if you were plyaing on a 800x600.. the x-axis would be 13.333 (1.333 x 10).

By default 100 pixels = 1 unit. and 0,0 is the middle of the screen so on a default 800x600 the x,y positions are -6.666,-5 on the bottom left to +6.666,5 on the top right

This script just makes a bunch of sprites on the fly based on the width/height of your CA and scales them to fit. It then keeps a list of the SpriteRenderers so we can set the color. I added a SetColor method to change the color of any cell.

10. ### EternalAmbiguity

Joined:
Dec 27, 2014
Posts:
2,526
Many thanks. I really appreciate the help.

11. ### Kiwasi

Joined:
Dec 5, 2013
Posts:
16,734
Particle effects can also be useful for visualising this kind of thing. And can be dramatically cheaper then a bunch of GameObjects.

12. ### EternalAmbiguity

Joined:
Dec 27, 2014
Posts:
2,526
Well I tried commenting out the code which changes material color as a test in my current implementation, and it didn't have any significant effect on the speed. I'm currently in the process of re-writing it (with the help of someone I know in "real life") to make the iterative loop as short as possible and make it all modular (so the same short set of code works for 2 cell types, 3, 4, etc.).

Though I don't know if the simple fact that the code is working on X (10,000 in my case, and I want to go up to at least 1,000,000) GameObjects rather than something else is the slow part. Can you instantiate particle effects like that, and have them all follow a script independently?

13. ### JoeStrout

Joined:
Jan 14, 2011
Posts:
9,054
Sorry for chiming in late here, but if you want to display a big grid of colors efficiently, it'll be hard to beat PixelSurface.

I've used it to implement cellular automata before, such as in the simple fluid simulation described (with source code!) about halfway through this thread.

Full disclosure: I wrote PixelSurface, so I may be considered biased. But I wrote it because I wanted to do the sort of thing you're doing, and needed an efficient way to display. So maybe you'll find it useful too.

14. ### EternalAmbiguity

Joined:
Dec 27, 2014
Posts:
2,526
Thanks. I appreciate all the suggestions.

15. ### EternalAmbiguity

Joined:
Dec 27, 2014
Posts:
2,526
Alright, so over the past couple of weeks I've managed to put a little something together...but I'm stumped at one point. I've been working on this for about 6 hours today, chipping away at little things here and there, and I've finally figured out WHAT exactly is going wrong...but I don't have any clue why it's happening.

See the relevant code here:

Code (csharp):
1. using System;
2. using System.Collections;
3. using System.Collections.Generic;
4. using System.IO;
5. using System.Linq;
7. using UnityEngine;
8. using UnityEngine.UI;
9. public class GridMake : MonoBehaviour
10. {
11.   GameObject tilePrefab;
12.   public float gridWidth, gridHeight;
13.   GameObject[] tiles;
14.   GameObject[,] gridOfCells;
15.   public GameObject gridCheck;
16.   public float time = 0.0f;
17.   public float timeToWait = 0f;
18.   public int cellType;
19.   public int cellNumber;
20.   int maxIterations = 0;
21.   int currentIteration;
22.   public int arrayCopyRangeStart;
23.   int result;
24.   public float trueScale;
25.   public float scaleHeight;
26.   public float scaleWidth;
27.   int maxRuns = 0;
28.   int currentRun;
29.   GameObject IterationText;
30.   GameObject RunText;
31.   public Toggle saveFileCheck;
32.   Dropdown neighborhoodType;
33.   Dictionary<int, Color> dict;
34.   int[] newColors;
35.   Dropdown orderselect;
36.   int[] probs;
37.   int[] neighborProbs;
38.   int[] cellTypeTransitions;
39.   int xTilesCreated, yTilesCreated;
40.   public GameObject neighborUp;
41.   public GameObject neighborDown;
42.   public GameObject neighborLeft;
43.   public GameObject neighborRight;
44.   Color currentColor;
45.   GameObject basePage;
46.   GameObject probParent;
47.   GameObject currentInputField;
48.   public string inputFieldText;
49.   public int testNumber1;
50.   public int testNumber2 = 0;
51.   public int testNumber3;
52.   public int testNumber4;
53.   public string tempTransition;
54.   public string tempTransition2;
55.   public float tileDistance = 0.0f; //Spacing between tiles
56.   void Start()
57.   {
58.   }
59.   void Update()
60.   {
61.
62.   }
63.   public IEnumerator CreateTiles()
64.   {
65.   orderselect = GameObject.FindGameObjectWithTag("OrderDropDown").GetComponent<Dropdown>();
66.   cellNumber = int.Parse(GameObject.FindGameObjectWithTag("numbercelltype").GetComponent<Text>().text);
67.   maxIterations = int.Parse(GameObject.Find("IterationCount").GetComponent<Text>().text);
68.   maxRuns = int.Parse(GameObject.Find("RunCount").GetComponent<Text>().text);
69.   timeToWait = float.Parse(GameObject.Find("WaitTimeCount").GetComponent<Text>().text);
70.   IterationText = GameObject.Find("IterationText");
71.   RunText = GameObject.Find("RunText");
72.   newColors = new int[cellNumber];
73.   probs = new int[(cellNumber) * (cellNumber - 1)];
74.   int[] cellNumberCounts = new int[cellNumber];
75.   Array[] startingCellAmountGroups = new Array[cellNumber];
76.   neighborhoodType = GameObject.FindGameObjectWithTag("NeighborhoodDropdown").GetComponent<Dropdown>();
77.   if (orderselect.value == 0)
78.   {
79.   cellTypeTransitions = new int[(cellNumber) * (cellNumber - 1)];
80.   for (int i = 0; i < cellNumber; ++i)
81.   {
82.   string numstring = (i + 1).ToString();
83.   newColors[i] = GameObject.Find("NMP" + (orderselect.value + 1) + numstring).transform.Find("ColorDropdown").GetComponent<Dropdown>().value;
84.   GameObject basePage = GameObject.Find("NMP" + (orderselect.value + 1) + numstring);
85.   startingCellAmountGroups[i] = new GameObject[int.Parse(GameObject.Find("NMP" + (orderselect.value + 1) + numstring).transform.Find("StartNumberInputField").transform.Find("Text").GetComponent<Text>().text)];
86.   for (int j = 1; j < cellNumber; ++j)
87.   {
88.   string secondNumstring = (j).ToString();
89.   GameObject prob = basePage.transform.Find("ProbabilityScrollView").transform.Find("1stProbContent").gameObject;
90.   probs[((j + ((cellNumber - 1) * i)) - 1)] = Mathf.RoundToInt((float.Parse(prob.transform.Find("ProbInputField1." + secondNumstring).Find("ProbTransValue").GetComponent<Text>().text)) * 1000000000) * (int.MaxValue / 1000000000);
91.   string tempTransition = prob.transform.Find("ProbInputField1." + secondNumstring).transform.Find("ProbTransText").GetComponent<Text>().text.Remove(0, 33);
92.   string tempTransition2 = tempTransition.Remove(1, 1);
93.   int transitionNumber = int.Parse(tempTransition2);
94.   cellTypeTransitions[((j + ((cellNumber - 1) * i)) - 1)] = transitionNumber;
95.   }
96.   }
97.   //NOTE!!! The above sets a minimum probability value of 0.000000001. Changing to using floats might solve the problem.
98.   }
99.   if (orderselect.value == 1)
100.   {
101.   if (neighborhoodType.value == 0)
102.   {
103.   cellTypeTransitions = new int[((cellNumber - 1) * 5) * cellNumber];
104.   neighborProbs = new int[((cellNumber - 1) * 5) * cellNumber];
105.   for (int i = 0; i < cellNumber; ++i)
106.   {
107.   string numstring = (i + 1).ToString();
108.   basePage = GameObject.Find("NMP2" + numstring);
109.   probParent = basePage.transform.Find("ProbabilityScrollView").transform.Find("1stProbContent").gameObject;
110.   startingCellAmountGroups[i] = new GameObject[int.Parse(GameObject.Find("NMP" + (orderselect.value + 1) + numstring).transform.Find("StartNumberInputField").transform.Find("Text").GetComponent<Text>().text)];
111.   newColors[i] = GameObject.Find("NMP2" + numstring).transform.Find("ColorDropdown").GetComponent<Dropdown>().value;
112.   for (int j = 0; j < (cellNumber - 1); ++j)
113.   {
114.   for (int k = 0; k < 5; ++k)
115.   {
116.   string secondNumString = (j + 1).ToString();
117.   currentInputField = probParent.transform.Find("ProbInputField2." + secondNumString + "." + k.ToString()).gameObject;
118.   inputFieldText = currentInputField.transform.Find("ProbTransValue").GetComponent<Text>().text;
119.   neighborProbs[(i * ((cellNumber - 1) * 5)) + (j * 5) + k] = Mathf.RoundToInt((float.Parse(probParent.transform.Find("ProbInputField2." + secondNumString + "." + k.ToString()).transform.Find("ProbTransValue").GetComponent<Text>().text)) * 1000000000) * (int.MaxValue / 1000000000);
120.   tempTransition = probParent.transform.Find("ProbInputField2." + secondNumString + "." + k).transform.Find("ProbTransText").GetComponent<Text>().text.Remove(0, 61);
121.   tempTransition2 = tempTransition.Remove(1, 8);
122.   int transitionNumber = int.Parse(tempTransition2);
123.   cellTypeTransitions[(i * ((cellNumber - 1) * 5)) + (j * 5) + k] = transitionNumber;
124.   }
125.   }
126.   }
127.   }
128.   }
129.   //This dictionary only uses the Unity-named colors because it's easier to pick these in a drop-down than to have a color selector.
130.   //Future updates might adjust the UI page to allow for direct color selection, which could then be passed to this script and avoid the artificial limitations here.
131.   //After all, this can only show up to 9 cell types. Any number is possible, but with only 9 colors you can only differentiate between nine types.
132.   dict = new Dictionary<int, Color>();
142.   //UI stuff and grid formation
143.   {
144.   tilePrefab = GameObject.FindGameObjectWithTag("tilePrefab");
145.   gridWidth = int.Parse(GameObject.Find("Horigrid").GetComponent<Text>().text);
146.   gridHeight = int.Parse(GameObject.Find("Vertgrid").GetComponent<Text>().text);
147.   float xOffset = 0.0f, yOffset = 0.0f;
148.   scaleHeight = ((Screen.height / (Screen.height / 10)) / gridHeight);
149.   scaleWidth = ((Screen.width / (Screen.width / 10)) / gridWidth);
150.   trueScale = Mathf.Min(scaleHeight, scaleWidth);
151.   tilePrefab.transform.localScale = new Vector3(trueScale, trueScale, trueScale);
152.   gridOfCells = new GameObject[Convert.ToInt32(Math.Ceiling(gridWidth)), Convert.ToInt32(Math.Ceiling(gridHeight))];
153.   for (xTilesCreated = 0; xTilesCreated < gridWidth; xTilesCreated += 1)
154.   {
155.   for (yTilesCreated = 0; yTilesCreated < gridHeight; yTilesCreated += 1)
156.   {
157.   Vector3 position = new Vector3(transform.position.x + xOffset + (trueScale / 2), transform.position.y + yOffset + (trueScale / 2), transform.position.z);
158.   GameObject tile = Instantiate(tilePrefab, position, Quaternion.identity) as GameObject;
159.   yOffset += trueScale + tileDistance;
160.   tile.name = ("Cell" + ((xTilesCreated * gridWidth) + yTilesCreated));
161.   tile.tag = ("Cells");
162.   gridOfCells[xTilesCreated, yTilesCreated] = tile;
163.   SetNeighbor();
164.   }
165.   xOffset += trueScale + tileDistance;
166.   yOffset = 0.0f;
167.   }
168.   tilePrefab.GetComponent<Renderer>().enabled = false;
169.   tiles = GameObject.FindGameObjectsWithTag("Cells");
170.   foreach (GameObject tile in tiles)
171.   {
172.   tile.transform.localPosition = new Vector3((tile.transform.position.x - (gridWidth / 2) * trueScale), (tile.transform.position.y - ((gridHeight / 2)) * trueScale), tile.transform.position.z);
173.   tile.GetComponent<Renderer>().enabled = true;
174.   }
175.
176.   }
177.   gridCheck = gridOfCells[1, 0];
178.   //This sets up the probability array to have the appropriate values for probability ranges
179.   if (orderselect.value == 0)
180.   {
181.   for (int i = 0; i < (cellNumber * (cellNumber - 1)); i += (cellNumber - 1))
182.   {
183.   for (int j = (i + (cellNumber - 2)); j > (i - 1); --j)
184.   {
185.   for (int k = (j - 1); k > (i - 1); --k)
186.   {
187.   probs[j] = probs[j] + probs[k];
188.   }
189.   }
190.   }
191.   }
192.   //This is the actual iteration loop
193.   for (currentRun = 1; currentRun < (maxRuns + 1); ++currentRun)
194.   {
195.   //This first shuffles the group of objects, then changes the colors of the objects based on cell type starting amounts
196.   tiles.Shuffle();
197.   for (int i = 0; i < cellNumber; ++i)
198.   {
199.   arrayCopyRangeStart = 0;
200.   int j = 0;
201.   for (int k = (i - 1); k > (-1); --k)
202.   {
203.   arrayCopyRangeStart = j + startingCellAmountGroups[k].Length;
204.   j = arrayCopyRangeStart;
205.   }
206.   Array.Copy(tiles, arrayCopyRangeStart, startingCellAmountGroups[i], 0, startingCellAmountGroups[i].Length);
207.   foreach (GameObject tile in startingCellAmountGroups[i])
208.   {
209.   tile.GetComponent<Renderer>().material.color = dict[newColors[i]];
210.   cellType = (i + 1);
211.   }
212.   }
213.   for (currentIteration = 0; currentIteration < (maxIterations); currentIteration++)
214.   {
215.   tiles.Shuffle();
216.   for (int k = 0; k < cellNumber; ++k) cellNumberCounts[k] = 0;
217.   foreach (GameObject tile in tiles)
218.   {
219.   if (orderselect.value == 1)
220.   {
221.   CheckNeighbors();
222.   //organizeProbs();
223.   testNumber1 = probs[1];
224.   }
225.
226.   result = UnityEngine.Random.Range(0, int.MaxValue);
227.   int i = ((cellType - 1) * (cellNumber - 1));
228.   {
229.   for (int j = (i + (cellNumber - 2)); j > i; --j)
230.   {
231.   if (result <= probs[j] && result > probs[j - 1])
232.   {
233.   tile.GetComponent<Renderer>().material.color = dict[newColors[(cellTypeTransitions[j] - 1)]];
234.   cellType = cellTypeTransitions[j];
235.   goto Label;
236.   }
237.   }
238.   if (result <= probs[i])
239.   {
240.   tile.GetComponent<Renderer>().material.color = dict[newColors[(cellTypeTransitions[i] - 1)]];
241.   cellType = cellTypeTransitions[i];
242.   goto Label;
243.   }
244.   }
245.   Label:
246.   ;
247.   cellNumberCounts[(cellType - 1)] += 1;
248.   }
249.
250.   if (saveFileCheck.isOn == true)
251.   {
252.   using (StreamWriter wt = File.AppendText(Application.persistentDataPath + "Run " + currentRun + ".txt"))
253.   {
254.   wt.Write("Iteration: " + (currentIteration + 1));
255.   for (int i = 0; i < cellNumber; ++i)
256.   {
257.   string cellTypeString = (i + 1).ToString();
258.   wt.Write(" Cell Type " + cellTypeString + ": " + cellNumberCounts[i]);
259.   }
260.   wt.WriteLine();
261.   wt.Close();
262.   }
263.   }
264.   IterationText.GetComponent<Text>().text = "Iteration: " + (currentIteration + 1).ToString();
265.   RunText.GetComponent<Text>().text = "Run: " + currentRun.ToString();
266.   yield return new WaitForSeconds(timeToWait);
267.   }
268.   }
269.   }
270.   void organizeProbs()
271.   {
272.   for (int i = 0; i < (cellNumber * (cellNumber - 1)); i += (cellNumber - 1))
273.   {
274.   for (int j = (i + (cellNumber - 2)); j > (i - 1); --j)
275.   {
276.   for (int k = (j - 1); k > (i - 1); --k)
277.   {
278.   probs[j] = probs[j] + probs[k];
279.   }
280.   }
281.   }
282.   }
283.   void SetNeighbor()
284.   {
285.   if (orderselect.value != 0)
286.   {
287.   // if (gridType.value == 0)
288.   if (neighborhoodType.value == 0)
289.   {
290.   try
291.   {
292.   neighborUp = gridOfCells[(xTilesCreated), (yTilesCreated + 1)];
293.   }
294.   catch (System.IndexOutOfRangeException)
295.   {
296.   }
297.   try
298.   {
299.   neighborDown = gridOfCells[(xTilesCreated), (yTilesCreated - 1)];
300.   }
301.   catch (System.IndexOutOfRangeException)
302.   {
303.   }
304.   try
305.   {
306.   neighborLeft = gridOfCells[(xTilesCreated - 1), (yTilesCreated)];
307.   }
308.   catch (System.IndexOutOfRangeException)
309.   {
310.   }
311.   try
312.   {
313.   neighborRight = gridOfCells[(xTilesCreated + 1), (yTilesCreated)];
314.   }
315.   catch (System.IndexOutOfRangeException)
316.   {
317.   }
318.   }
319.   }
320.   else
321.   {
322.   }
323.   }
324.   void CheckNeighbors()
325.   {
326.   Color upColor = Color.clear;
327.   Color downColor = Color.clear;
328.   Color leftColor = Color.clear;
329.   Color rightColor = Color.clear;
330.   try
331.   {
332.   upColor = neighborUp.GetComponent<Renderer>().material.color;
333.   }
334.   catch (System.NullReferenceException)
335.   {
336.   }
337.   try
338.   {
339.   downColor = neighborDown.GetComponent<Renderer>().material.color;
340.   }
341.   catch (System.NullReferenceException)
342.   {
343.   }
344.   try
345.   {
346.   leftColor = neighborLeft.GetComponent<Renderer>().material.color;
347.   }
348.   catch (System.NullReferenceException)
349.   {
350.   }
351.   try
352.   {
353.   rightColor = neighborRight.GetComponent<Renderer>().material.color;
354.   }
355.   catch (System.NullReferenceException)
356.   {
357.   }
358.   Color[] vonNeumannGroup = new Color[4] { upColor, downColor, leftColor, rightColor };
359.   List<Color> filteredVonNeumann = new List<Color>();
360.   for (int currentCheck = 0; currentCheck < cellNumber; ++currentCheck)
361.   {
362.   int tempValue = 0;
363.   if (currentCheck == (cellType - 1)) continue;
364.   if (currentCheck < (cellType - 1))
365.   {
366.   for (int j = 0; j < 4; ++j)
367.   {
368.   if (vonNeumannGroup[j] == dict[newColors[currentCheck]])
369.   {
371.   }
372.   }
373.   if (filteredVonNeumann.Count == 4)
374.   {
375.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 4];
376.   }
377.   if (filteredVonNeumann.Count == 3)
378.   {
379.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 3];
380.   }
381.   if (filteredVonNeumann.Count == 2)
382.   {
383.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 2];
384.   }
385.   if (filteredVonNeumann.Count == 1)
386.   {
387.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 1];
388.   }
389.   if (filteredVonNeumann.Count == 0)
390.   {
391.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5))];
392.   }
393.   probs[((cellType - 1) * (cellNumber - 1)) + currentCheck] = tempValue;
394.   }
395.   if (currentCheck > (cellType - 1))
396.   {
397.   for (int j = 0; j < 4; ++j)
398.   {
399.   if (vonNeumannGroup[j] == dict[newColors[currentCheck]])
400.   {
402.   }
403.   }
404.   if (filteredVonNeumann.Count == 4)
405.   {
406.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 4];
407.   }
408.   if (filteredVonNeumann.Count == 3)
409.   {
410.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 3];
411.   }
412.   if (filteredVonNeumann.Count == 2)
413.   {
414.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 2];
415.   }
416.   if (filteredVonNeumann.Count == 1)
417.   {
418.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 1];
419.   }
420.   if (filteredVonNeumann.Count == 0)
421.   {
422.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5))];
423.   }
424.   probs[((cellType - 1) * (cellNumber - 1)) + (currentCheck - 1)] = tempValue;
425.   }
426.   testNumber4 = probs[((cellType - 1) * (cellNumber - 1)) + (currentCheck - 1)];
427.   testNumber3 = probs.Length;
428.   }
429.   }
430. }
432. {
434.   private static System.Random Local;
436.   {
438.   }
439. }
440. static class MyExtensions
441. {
442.   public static void Shuffle<T>(this IList<T> list)
443.   {
444.   int n = list.Count;
445.   while (n > 1)
446.   {
447.   n--;
449.   T value = list[k];
450.   list[k] = list[n];
451.   list[n] = value;
452.   }
453.   }
454. }
The project is also attached if needed to better understand things. I realize the code is kind of jumbled, but I'll point out the important parts.

I'll describe the issue. A "1-dimensional" grid works fine, but this is all for what I term a "1.5-dimensional" grid, a grid where each cell has neighbor effects but does not move.

The user enters some number of cell types. They enter a bunch of other data about making the grid, and input the probabilities of conversions between cell types. This includes the probabilities based on the number of neighbors of a certain type. They also pick how many cells of each cell type the grid starts with.

The grid is formed. At this point, each tile finds its neighbors (up, down, left, and right), with edge cases simply skipping the out-of-bounds areas. The tiles are turned into the user-chosen amounts of each cell type.

The iteration begins. Each tile checks its neighbors to see if they are of a certain cell type. If they are, a count is made (from 0 to 4, because 4 neighbors). The count is checked, then the appropriate probability from the ones the user entered is chosen.

Next, that chosen probability is put into a smaller group of numbers which only includes the few probabilities needed for the cell after all neighbors have been determined.

Basically, there's an array of possible probabilities, and I move the appropriate value to the array of needed probabilities. This starts on line 363 or so.

I can query the array of possible probabilities and return values. I can query the array of needed probabilities using the formula within the loop used to assign values, and return values. But I cannot query an index number and return a value. I get 0, while the other method (using the special formula) gives a number.

If one wanted to replicate the issue in the project:
start it up. Select New CA or whatever. Select Order: 1.5 in dropdown. Select grid size (10 x 10 is simple). Select number of cell types (I typically test with 3). Select iterations, runs, time to wait between iterations (I typically do 1000, 1, 0). Click Next on bottom right.

Select three different colors for next three (or however many cell types you have) pages, and put probability values for the input fields (note! Scroll down or click-and-drag inside the field to move down). Make sure to put starting values of each cell type, being sure not to go over grid size. Click Next on the final page to confirm your selections. Select Yes.

The iterations will begin, but everything turns into 1 cell type for some reason I cannot ascertain. In any case, depending on your probability values chosen, you should have other colors showing up, but they don't.

This is utterly mind-boggling to me, and if anyone can help me figure it out I would sincerely appreciate it.

#### Attached Files:

• ###### Cellular Automata.7z
File size:
2.8 MB
Views:
90
Last edited: Feb 9, 2017
16. ### takatok

Joined:
Aug 18, 2016
Posts:
1,496
I was able to download your project and run it. As this is a pretty big project so far, I'm going to avoid any suggestions on changing things to work in a "better" structured way. I'll limit my responses to getting existing code up and running. If your interested in my opinions on problems with how the code is structured and better ways to go about it, feel free to start up a Conversation with me.

1) You don't have a backup array that you actually do the calculations on. You are writing directly to the main cell array as you change states of things. Say we are on iteration 0, the start. Cell[0,0] will get changed to its correct state for Iteration 1. However, Cell[1,0] will now be using Cell[0,0] state from Iteration 1, not the correct one from Iteration 0 (its lost). The standard way to do CA is to have a backup grid that you copy the entire cell grid into. Then when you process Cell[X,Y] you check its neighbors in backup grid and write its new state into the main grid. This way you preserve a copy of the old iteration to correctly asses the new iteration.

2) ***I'm positive this problem is why it goes to 1 color*** You UpNeighbor, DownNeighbor, etc Gameobjects are just single variables they aren't arrays. So each time you call SetNeighbor you only setting them to the neighbors of the currentCell. When you finally get done creating the initial grid, those 4 GameObjects are the neighbors of the last cell created Tile[width-1,height-1]. So your entire CA is just using those neighbors for every cell. If it happens to be that those neighbors indicate the cell should turn a particular state.. you entire grid will slowly go to that state. The only reason it doesn't instantly turn to 1 color is your doing a probabilistic chance that it might turn that color. You need to change SetNeighbors to take an x,y co-ordinate and set those objects based on that X,Y. You can take out the call to SetNeighbors in the grid creation loop. You need to call it in the iteration loop every time right before you call GetNeighbors.

17. ### EternalAmbiguity

Joined:
Dec 27, 2014
Posts:
2,526
Yeah, I know it definitely needs optimization. But for the time being I'm mainly just trying to get it to work first.

1. Hmm, yeah I see what you mean. So in essence the grid changes each time a cell is changed, even within an iteration. I'll have to look at how to fix this.

2. Dang, of course. For some reason I thought it was applying those values to the cells like it does for the cell type, but that isn't necessarily the case.

I'll try moving the SetNeighbors part, then see what happens. Thanks again for the help.

Edit: well, in the SetNeighbors part I use parameters from the grid formation. To change that I would have to query the current location of the object within the gridofcells array, then do the neighbor calculation. Since my knowledge of code is absurdly small I don't exactly know how to do that first part. However, there's no need to--since doing SetNeighbors during iteration is a terrible idea (it only needs to be done once).

I'll just form a new array within which to store the neighbors like you pointed out.

GameObject[] neighborArray = [(((gridHeight - 1) * (gridWidth - 1)) * 4) + (((gridHeight * gridWidth) - 4) * 3) + (4 * 2)]

After I get that done I'll update if it works.

Edit: ...

GameObject[] neighborArray = new GameObject[(((intGridHeight - 2) * (intGridWidth - 2)) * 4) + ((((2 * intGridHeight) + (2 * intGridWidth)) - 4) * 3) + (4 * 2)];

Last edited: Feb 9, 2017
18. ### EternalAmbiguity

Joined:
Dec 27, 2014
Posts:
2,526
Okay, I decided to go with an array that's simply (gridHeight x gridWidth) * 4, rather than deal with any issues with appropriately finding edge cases. It makes the array somewhat larger, but I don't think that's a concern, at this stage at least.

Here's the new GridMake code:
Code (csharp):
1. using System;
2. using System.Collections;
3. using System.Collections.Generic;
4. using System.IO;
5. using System.Linq;
7. using UnityEngine;
8. using UnityEngine.UI;
9. public class GridMake : MonoBehaviour
10. {
11.   GameObject tilePrefab;
12.   GameObject IterationText;
13.   GameObject RunText;
14.   GameObject basePage;
15.   GameObject probParent;
16.   GameObject currentInputField;
17.   public GameObject neighborUp;
18.   public GameObject neighborDown;
19.   public GameObject neighborLeft;
20.   public GameObject neighborRight;
21.   public GameObject gridCheck;
22.   GameObject[] neighborArray;
23.   GameObject[] tiles;
24.   GameObject[,] gridOfCells;
25.   public float gridWidth, gridHeight;
26.   public float time = 0.0f;
27.   public float timeToWait = 0f;
28.   public int cellType;
29.   public int cellNumber;
30.   int maxIterations = 0;
31.   int currentIteration;
32.   public int arrayCopyRangeStart;
33.   int result;
34.   public float trueScale;
35.   public float scaleHeight;
36.   public float scaleWidth;
37.   int maxRuns = 0;
38.   int currentRun;
39.   public Toggle saveFileCheck;
40.   Dropdown neighborhoodType;
41.   Dictionary<int, Color> dict;
42.   int[] newColors;
43.   Dropdown orderselect;
44.   int[] probs;
45.   int[] neighborProbs;
46.   int[] cellTypeTransitions;
47.   int xTilesCreated, yTilesCreated;
48.
49.   int intGridHeight;
50.   int intGridWidth;
51.   public int currentCellNumber;
52.   Color currentColor;
53.   public string inputFieldText;
54.   public int testNumber1;
55.   public int testNumber2 = 0;
56.   public int testNumber3;
57.   public int testNumber4;
58.   public string tempTransition;
59.   public string tempTransition2;
60.   public float tileDistance = 0.0f; //Spacing between tiles
61.   void Start()
62.   {
63.   }
64.   void Update()
65.   {
66.
67.   }
68.   public IEnumerator CreateTiles()
69.   {
70.   orderselect = GameObject.FindGameObjectWithTag("OrderDropDown").GetComponent<Dropdown>();
71.   cellNumber = int.Parse(GameObject.FindGameObjectWithTag("numbercelltype").GetComponent<Text>().text);
72.   maxIterations = int.Parse(GameObject.Find("IterationCount").GetComponent<Text>().text);
73.   maxRuns = int.Parse(GameObject.Find("RunCount").GetComponent<Text>().text);
74.   timeToWait = float.Parse(GameObject.Find("WaitTimeCount").GetComponent<Text>().text);
75.   IterationText = GameObject.Find("IterationText");
76.   RunText = GameObject.Find("RunText");
77.   newColors = new int[cellNumber];
78.   probs = new int[(cellNumber) * (cellNumber - 1)];
79.   int[] cellNumberCounts = new int[cellNumber];
80.   Array[] startingCellAmountGroups = new Array[cellNumber];
81.   neighborhoodType = GameObject.FindGameObjectWithTag("NeighborhoodDropdown").GetComponent<Dropdown>();
82.   if (orderselect.value == 0)
83.   {
84.   cellTypeTransitions = new int[(cellNumber) * (cellNumber - 1)];
85.   for (int i = 0; i < cellNumber; ++i)
86.   {
87.   string numstring = (i + 1).ToString();
88.   newColors[i] = GameObject.Find("NMP" + (orderselect.value + 1) + numstring).transform.Find("ColorDropdown").GetComponent<Dropdown>().value;
89.   GameObject basePage = GameObject.Find("NMP" + (orderselect.value + 1) + numstring);
90.   startingCellAmountGroups[i] = new GameObject[int.Parse(GameObject.Find("NMP" + (orderselect.value + 1) + numstring).transform.Find("StartNumberInputField").transform.Find("Text").GetComponent<Text>().text)];
91.   for (int j = 1; j < cellNumber; ++j)
92.   {
93.   string secondNumstring = (j).ToString();
94.   GameObject prob = basePage.transform.Find("ProbabilityScrollView").transform.Find("1stProbContent").gameObject;
95.   probs[((j + ((cellNumber - 1) * i)) - 1)] = Mathf.RoundToInt((float.Parse(prob.transform.Find("ProbInputField1." + secondNumstring).Find("ProbTransValue").GetComponent<Text>().text)) * 1000000000) * (int.MaxValue / 1000000000);
96.   string tempTransition = prob.transform.Find("ProbInputField1." + secondNumstring).transform.Find("ProbTransText").GetComponent<Text>().text.Remove(0, 33);
97.   string tempTransition2 = tempTransition.Remove(1, 1);
98.   int transitionNumber = int.Parse(tempTransition2);
99.   cellTypeTransitions[((j + ((cellNumber - 1) * i)) - 1)] = transitionNumber;
100.   }
101.   }
102.   //NOTE!!! The above sets a minimum probability value of 0.000000001. Changing to using floats might solve the problem.
103.   }
104.   if (orderselect.value == 1)
105.   {
106.   if (neighborhoodType.value == 0)
107.   {
108.   cellTypeTransitions = new int[((cellNumber - 1) * 5) * cellNumber];
109.   neighborProbs = new int[((cellNumber - 1) * 5) * cellNumber];
110.   for (int i = 0; i < cellNumber; ++i)
111.   {
112.   string numstring = (i + 1).ToString();
113.   basePage = GameObject.Find("NMP2" + numstring);
114.   probParent = basePage.transform.Find("ProbabilityScrollView").transform.Find("1stProbContent").gameObject;
115.   startingCellAmountGroups[i] = new GameObject[int.Parse(GameObject.Find("NMP" + (orderselect.value + 1) + numstring).transform.Find("StartNumberInputField").transform.Find("Text").GetComponent<Text>().text)];
116.   newColors[i] = GameObject.Find("NMP2" + numstring).transform.Find("ColorDropdown").GetComponent<Dropdown>().value;
117.   for (int j = 0; j < (cellNumber - 1); ++j)
118.   {
119.   for (int k = 0; k < 5; ++k)
120.   {
121.   string secondNumString = (j + 1).ToString();
122.   currentInputField = probParent.transform.Find("ProbInputField2." + secondNumString + "." + k.ToString()).gameObject;
123.   inputFieldText = currentInputField.transform.Find("ProbTransValue").GetComponent<Text>().text;
124.   neighborProbs[(i * ((cellNumber - 1) * 5)) + (j * 5) + k] = Mathf.RoundToInt((float.Parse(probParent.transform.Find("ProbInputField2." + secondNumString + "." + k.ToString()).transform.Find("ProbTransValue").GetComponent<Text>().text)) * 1000000000) * (int.MaxValue / 1000000000);
125.   tempTransition = probParent.transform.Find("ProbInputField2." + secondNumString + "." + k).transform.Find("ProbTransText").GetComponent<Text>().text.Remove(0, 61);
126.   tempTransition2 = tempTransition.Remove(1, 8);
127.   int transitionNumber = int.Parse(tempTransition2);
128.   cellTypeTransitions[(i * ((cellNumber - 1) * 5)) + (j * 5) + k] = transitionNumber;
129.   }
130.   }
131.   }
132.   }
133.   }
134.   //This dictionary only uses the Unity-named colors because it's easier to pick these in a drop-down than to have a color selector.
135.   //Future updates might adjust the UI page to allow for direct color selection, which could then be passed to this script and avoid the artificial limitations here.
136.   //After all, this can only show up to 9 cell types. Any number is possible, but with only 9 colors you can only differentiate between nine types.
137.   dict = new Dictionary<int, Color>();
147.   //UI stuff and grid formation
148.   {
149.   tilePrefab = GameObject.FindGameObjectWithTag("tilePrefab");
150.   gridWidth = int.Parse(GameObject.Find("Horigrid").GetComponent<Text>().text);
151.   gridHeight = int.Parse(GameObject.Find("Vertgrid").GetComponent<Text>().text);
152.   intGridWidth = Convert.ToInt32(Math.Ceiling(gridWidth));
153.   intGridHeight = Convert.ToInt32(Math.Ceiling(gridHeight));
154.   float xOffset = 0.0f, yOffset = 0.0f;
155.   scaleHeight = ((Screen.height / (Screen.height / 10)) / gridHeight);
156.   scaleWidth = ((Screen.width / (Screen.width / 10)) / gridWidth);
157.   trueScale = Mathf.Min(scaleHeight, scaleWidth);
158.   tilePrefab.transform.localScale = new Vector3(trueScale, trueScale, trueScale);
159.   gridOfCells = new GameObject[intGridWidth, intGridHeight];
160.   for (xTilesCreated = 0; xTilesCreated < gridWidth; xTilesCreated += 1)
161.   {
162.   for (yTilesCreated = 0; yTilesCreated < gridHeight; yTilesCreated += 1)
163.   {
164.   Vector3 position = new Vector3(transform.position.x + xOffset + (trueScale / 2), transform.position.y + yOffset + (trueScale / 2), transform.position.z);
165.   GameObject tile = Instantiate(tilePrefab, position, Quaternion.identity) as GameObject;
166.   yOffset += trueScale + tileDistance;
167.   tile.name = ("Cell" + ((xTilesCreated * gridWidth) + yTilesCreated));
168.   tile.tag = ("Cells");
169.   gridOfCells[xTilesCreated, yTilesCreated] = tile;
170.   SetNeighbor();
171.   }
172.   xOffset += trueScale + tileDistance;
173.   yOffset = 0.0f;
174.   }
175.   tilePrefab.GetComponent<Renderer>().enabled = false;
176.   tiles = GameObject.FindGameObjectsWithTag("Cells");
177.   foreach (GameObject tile in tiles)
178.   {
179.   tile.transform.localPosition = new Vector3((tile.transform.position.x - (gridWidth / 2) * trueScale), (tile.transform.position.y - ((gridHeight / 2)) * trueScale), tile.transform.position.z);
180.   tile.GetComponent<Renderer>().enabled = true;
181.   }
182.
183.   }
184.   gridCheck = gridOfCells[1, 0];
185.   //This sets up the probability array to have the appropriate values for probability ranges
186.   if (orderselect.value == 0)
187.   {
188.   for (int i = 0; i < (cellNumber * (cellNumber - 1)); i += (cellNumber - 1))
189.   {
190.   for (int j = (i + (cellNumber - 2)); j > (i - 1); --j)
191.   {
192.   for (int k = (j - 1); k > (i - 1); --k)
193.   {
194.   probs[j] = probs[j] + probs[k];
195.   }
196.   }
197.   }
198.   }
199.   //This is the actual iteration loop
200.   for (currentRun = 1; currentRun < (maxRuns + 1); ++currentRun)
201.   {
202.   //This first shuffles the group of objects, then changes the colors of the objects based on cell type starting amounts
203.   tiles.Shuffle();
204.   for (int i = 0; i < cellNumber; ++i)
205.   {
206.   arrayCopyRangeStart = 0;
207.   int j = 0;
208.   for (int k = (i - 1); k > (-1); --k)
209.   {
210.   arrayCopyRangeStart = j + startingCellAmountGroups[k].Length;
211.   j = arrayCopyRangeStart;
212.   }
213.   Array.Copy(tiles, arrayCopyRangeStart, startingCellAmountGroups[i], 0, startingCellAmountGroups[i].Length);
214.   foreach (GameObject tile in startingCellAmountGroups[i])
215.   {
216.   tile.GetComponent<Renderer>().material.color = dict[newColors[i]];
217.   cellType = (i + 1);
218.   }
219.   }
220.   for (currentIteration = 0; currentIteration < (maxIterations); currentIteration++)
221.   {
222.   tiles.Shuffle();
223.   for (int k = 0; k < cellNumber; ++k) cellNumberCounts[k] = 0;
224.   foreach (GameObject tile in tiles)
225.   {
226.   if (orderselect.value == 1)
227.   {
228.   currentCellNumber = int.Parse(tile.name.Remove(0, 4));
229.   CheckNeighbors();
230.   organizeProbs();
231.   testNumber1 = probs[1];
232.   }
233.
234.   result = UnityEngine.Random.Range(0, int.MaxValue);
235.   int i = ((cellType - 1) * (cellNumber - 1));
236.   {
237.   for (int j = (i + (cellNumber - 2)); j > i; --j)
238.   {
239.   if (result <= probs[j] && result > probs[j - 1])
240.   {
241.   tile.GetComponent<Renderer>().material.color = dict[newColors[(cellTypeTransitions[j] - 1)]];
242.   cellType = cellTypeTransitions[j];
243.   goto Label;
244.   }
245.   }
246.   if (result <= probs[i])
247.   {
248.   tile.GetComponent<Renderer>().material.color = dict[newColors[(cellTypeTransitions[i] - 1)]];
249.   cellType = cellTypeTransitions[i];
250.   goto Label;
251.   }
252.   }
253.   Label:
254.   ;
255.   cellNumberCounts[(cellType - 1)] += 1;
256.   }
257.
258.   if (saveFileCheck.isOn == true)
259.   {
260.   using (StreamWriter wt = File.AppendText(Application.persistentDataPath + "Run " + currentRun + ".txt"))
261.   {
262.   wt.Write("Iteration: " + (currentIteration + 1));
263.   for (int i = 0; i < cellNumber; ++i)
264.   {
265.   string cellTypeString = (i + 1).ToString();
266.   wt.Write(" Cell Type " + cellTypeString + ": " + cellNumberCounts[i]);
267.   }
268.   wt.WriteLine();
269.   wt.Close();
270.   }
271.   }
272.   IterationText.GetComponent<Text>().text = "Iteration: " + (currentIteration + 1).ToString();
273.   RunText.GetComponent<Text>().text = "Run: " + currentRun.ToString();
274.   yield return new WaitForSeconds(timeToWait);
275.   }
276.   }
277.   }
278.   void organizeProbs()
279.   {
280.   for (int i = 0; i < (cellNumber * (cellNumber - 1)); i += (cellNumber - 1))
281.   {
282.   for (int j = (i + (cellNumber - 2)); j > (i - 1); --j)
283.   {
284.   for (int k = (j - 1); k > (i - 1); --k)
285.   {
286.   probs[j] = probs[j] + probs[k];
287.   }
288.   }
289.   }
290.   }
291.   void SetNeighbor()
292.   {
293.   if (orderselect.value != 0)
294.   {
295.   // if (gridType.value == 0)
296.   if (neighborhoodType.value == 0)
297.   {
298.   neighborArray = new GameObject[(intGridHeight * intGridWidth) * 4];
299.   try
300.   {
301.   neighborUp = gridOfCells[(xTilesCreated), (yTilesCreated + 1)];
302.   neighborArray[((((xTilesCreated * intGridWidth) + yTilesCreated) * 4) + 0)] = neighborUp;
303.   }
304.   catch (System.IndexOutOfRangeException)
305.   {
306.   }
307.   try
308.   {
309.   neighborDown = gridOfCells[(xTilesCreated), (yTilesCreated - 1)];
310.   neighborArray[((((xTilesCreated * intGridWidth) + yTilesCreated) * 4) + 1)] = neighborDown;
311.   }
312.   catch (System.IndexOutOfRangeException)
313.   {
314.   }
315.   try
316.   {
317.   neighborLeft = gridOfCells[(xTilesCreated - 1), (yTilesCreated)];
318.   neighborArray[((((xTilesCreated * intGridWidth) + yTilesCreated) * 4) + 2)] = neighborLeft;
319.   }
320.   catch (System.IndexOutOfRangeException)
321.   {
322.   }
323.   try
324.   {
325.   neighborRight = gridOfCells[(xTilesCreated + 1), (yTilesCreated)];
326.   neighborArray[((((xTilesCreated * intGridWidth) + yTilesCreated) * 4) + 3)] = neighborRight;
327.   }
328.   catch (System.IndexOutOfRangeException)
329.   {
330.   }
331.   }
332.   }
333.   else
334.   {
335.   }
336.   }
337.   void CheckNeighbors()
338.   {
339.   Color upColor = Color.clear;
340.   Color downColor = Color.clear;
341.   Color leftColor = Color.clear;
342.   Color rightColor = Color.clear;
343.   try
344.   {
345.   upColor = neighborArray[((currentCellNumber * 4) + 0)].GetComponent<Renderer>().material.color;
346.   }
347.   catch (System.NullReferenceException)
348.   {
349.   }
350.   try
351.   {
352.   downColor = neighborArray[((currentCellNumber * 4) + 1)].GetComponent<Renderer>().material.color;
353.   }
354.   catch (System.NullReferenceException)
355.   {
356.   }
357.   try
358.   {
359.   leftColor = neighborArray[((currentCellNumber * 4) + 2)].GetComponent<Renderer>().material.color;
360.   }
361.   catch (System.NullReferenceException)
362.   {
363.   }
364.   try
365.   {
366.   rightColor = neighborArray[((currentCellNumber * 4) + 3)].GetComponent<Renderer>().material.color;
367.   }
368.   catch (System.NullReferenceException)
369.   {
370.   }
371.   Color[] vonNeumannGroup = new Color[4] { upColor, downColor, leftColor, rightColor };
372.   List<Color> filteredVonNeumann = new List<Color>();
373.   for (int currentCheck = 0; currentCheck < cellNumber; ++currentCheck)
374.   {
375.   int tempValue = 0;
376.   if (currentCheck == (cellType - 1)) continue;
377.   if (currentCheck < (cellType - 1))
378.   {
379.   for (int j = 0; j < 4; ++j)
380.   {
381.   if (vonNeumannGroup[j] == dict[newColors[currentCheck]])
382.   {
384.   }
385.   }
386.   if (filteredVonNeumann.Count == 4)
387.   {
388.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 4];
389.   }
390.   if (filteredVonNeumann.Count == 3)
391.   {
392.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 3];
393.   }
394.   if (filteredVonNeumann.Count == 2)
395.   {
396.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 2];
397.   }
398.   if (filteredVonNeumann.Count == 1)
399.   {
400.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 1];
401.   }
402.   if (filteredVonNeumann.Count == 0)
403.   {
404.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5))];
405.   }
406.   probs[((cellType - 1) * (cellNumber - 1)) + currentCheck] = tempValue;
407.   }
408.   if (currentCheck > (cellType - 1))
409.   {
410.   for (int j = 0; j < 4; ++j)
411.   {
412.   if (vonNeumannGroup[j] == dict[newColors[currentCheck]])
413.   {
415.   }
416.   }
417.   if (filteredVonNeumann.Count == 4)
418.   {
419.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 4];
420.   }
421.   if (filteredVonNeumann.Count == 3)
422.   {
423.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 3];
424.   }
425.   if (filteredVonNeumann.Count == 2)
426.   {
427.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 2];
428.   }
429.   if (filteredVonNeumann.Count == 1)
430.   {
431.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 1];
432.   }
433.   if (filteredVonNeumann.Count == 0)
434.   {
435.   tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5))];
436.   }
437.   probs[((cellType - 1) * (cellNumber - 1)) + (currentCheck - 1)] = tempValue;
438.   }
439.   testNumber4 = probs[((cellType - 1) * (cellNumber - 1)) + (currentCheck - 1)];
440.   testNumber3 = probs.Length;
441.   }
442.   }
443. }
445. {
447.   private static System.Random Local;
449.   {
451.   }
452. }
453. static class MyExtensions
454. {
455.   public static void Shuffle<T>(this IList<T> list)
456.   {
457.   int n = list.Count;
458.   while (n > 1)
459.   {
460.   n--;
462.   T value = list[k];
463.   list[k] = list[n];
464.   list[n] = value;
465.   }
466.   }
467. }
I make the array, then assign the references to the neighbors for each cell. Later on I call the same position in the array. But I'm still getting the same effect as before, where everything returns to the one type. Any tips? I'll pore through it more thoroughly later on when I have some more time, but I wanted to put it out here in case there were any tips or errors people could point out.

19. ### takatok

Joined:
Aug 18, 2016
Posts:
1,496
A second thing to note about probability chances. Lets say we have 3 states. A cell is in State 0. It has 3 neighbors of state 1, that gives a 0.5 chance of turning to 1. It has 2 neighbors of state 2, that give a 0.8 chance of turning to state2.

Depending on how you check states and turn the cells, you could be giving a higher chance than needed to some states. Say you check the 0.5 and its true, then you check 0.8 and its true. Is the state set to 2 since that was the last state you checked? If so then the actual chance of state 1 happening is 0.5*.2 = 0.1

A better approach would be to but the chances in a list
0->1 = 0.5
0->2 = 0.8
0->0 = 0.1 (1-0.5)*(1.-0.8)

Now clearly these add to > 1.0 So we need to figure out the totalProbability (1.4) and pick a number between 0 and 1.4 then figure out what state we are in from that.

Given an array of floats above this code will produce the correct index
Code (CSharp):
1. private int GetStateFromProbability(float[] probChances)
2.     {
3.         float totalProb = 0;
4.         for (int i = 0; i < probChances.Length; ++i)
5.             totalProb += probChances[i];
6.         // this is here so we don't pick the actual last value
7.         // its a small chance 1 out of however big number the pseudorandom number generator uses
8.         // but its necessary for absolute toggles of 1.0 probabilities.  Otherwise super rare chance it will be wrong.
9.         totalProb -= .00001f;
10.         float[] pickRange = new float[probChances.Length];
11.
12.         pickRange[0] = 0;
13.         for (int i = 1; i < pickRange.Length; ++i)
14.             pickRange[i] = pickRange[i - 1] + probChances[i - 1];
15.         float pick = Random.Range(0, totalProb);
16.         int index = probChances.Length - 1;
17.         while (pickRange[index] > pick)
18.             index--;
19.         return index;
20.     }

20. ### EternalAmbiguity

Joined:
Dec 27, 2014
Posts:
2,526
That's a good point. I'll have to make an adjustment for that. I'll add it to the list. And by the way, I meant to say it earlier but I definitely want to talk to you later about optimization. And thanks for all the help.

So for some reason the problem I was having with not being able to return a numerical value (even though I could return a referenced value) disappeared...and appeared elsewhere.

First, though, I got that part where all cells would return to one type figured out. The new code is here:

Code (csharp):
1. using System;
2. using System.Collections;
3. using System.Collections.Generic;
4. using System.IO;
5. using System.Linq;
7. using UnityEngine;
8. using UnityEngine.UI;
9.
10. public class GridMake : MonoBehaviour
11. {
12.     GameObject tilePrefab;
13.     GameObject IterationText;
14.     GameObject RunText;
15.     GameObject basePage;
16.     GameObject probParent;
17.     GameObject currentInputField;
18.     public GameObject neighborUp;
19.     public GameObject neighborDown;
20.     public GameObject neighborLeft;
21.     public GameObject neighborRight;
22.     //GameObject gridCheck;
23.
24.     GameObject[] neighborArray;
25.     GameObject[] tiles;
26.     GameObject[,] gridOfCells;
27.
28.     float gridWidth, gridHeight;
29.     //float time = 0.0f;
30.     float timeToWait = 0f;
31.     public int cellType;
32.     int cellNumber;
33.     int maxIterations = 0;
34.     int currentIteration;
35.     public int arrayCopyRangeStart;
36.     int result;
37.     float trueScale;
38.     float scaleHeight;
39.     float scaleWidth;
40.     int maxRuns = 0;
41.     int currentRun;
42.     public Toggle saveFileCheck;
43.     Dropdown neighborhoodType;
44.     Dictionary<int, Color> dict;
45.     Dictionary<Color, int> inverseDict;
46.     int[] newColors;
47.     Dropdown orderselect;
48.     int[] probs;
49.     int[] neighborProbs;
50.     int[] cellTypeTransitions;
51.
52.     int xTilesCreated, yTilesCreated;
53.
54.
55.     int intGridHeight;
56.     int intGridWidth;
57.     public int currentCellNumber;
58.
59.     Color currentColor;
60.
61.     public string inputFieldText;
62.     public int testNumber1;
63.     public int testNumber2 = 0;
64.     public int testNumber3;
65.     public int testNumber4;
66.     public int testNumber5;
67.     public GameObject testGameObject;
68.     public Color neighborUpColor;
69.     public Color neighborDownColor;
70.     public Color neighborLeftColor;
71.     public Color neighborRightColor;
72.     public GameObject NewneighborUp;
73.     public GameObject NewneighborDown;
74.     public GameObject NewneighborLeft;
75.     public GameObject NewneighborRight;
76.     public Color upColor;
77.     public Color downColor;
78.     public Color leftColor;
79.     public Color rightColor;
80.     public string tempTransition;
81.     public string tempTransition2;
82.
83.     public float tileDistance = 0.0f; //Spacing between tiles
84.     void Start()
85.     {
86.
87.     }
88.
89.     void Update()
90.     {
91.
92.     }
93.
94.     public IEnumerator CreateTiles()
95.     {
96.         orderselect = GameObject.FindGameObjectWithTag("OrderDropDown").GetComponent<Dropdown>();
97.         cellNumber = int.Parse(GameObject.FindGameObjectWithTag("numbercelltype").GetComponent<Text>().text);
98.         maxIterations = int.Parse(GameObject.Find("IterationCount").GetComponent<Text>().text);
99.         maxRuns = int.Parse(GameObject.Find("RunCount").GetComponent<Text>().text);
100.         timeToWait = float.Parse(GameObject.Find("WaitTimeCount").GetComponent<Text>().text);
101.         IterationText = GameObject.Find("IterationText");
102.         RunText = GameObject.Find("RunText");
103.         newColors = new int[cellNumber];
104.         probs = new int[(cellNumber) * (cellNumber - 1)];
105.         int[] cellNumberCounts = new int[cellNumber];
106.         Array[] startingCellAmountGroups = new Array[cellNumber];
107.         neighborhoodType = GameObject.FindGameObjectWithTag("NeighborhoodDropdown").GetComponent<Dropdown>();
108.
109.         if (orderselect.value == 0)
110.         {
111.             cellTypeTransitions = new int[(cellNumber) * (cellNumber - 1)];
112.             for (int i = 0; i < cellNumber; ++i)
113.             {
114.                 string numstring = (i + 1).ToString();
115.                 newColors[i] = GameObject.Find("NMP" + (orderselect.value + 1) + numstring).transform.Find("ColorDropdown").GetComponent<Dropdown>().value;
116.                 GameObject basePage = GameObject.Find("NMP" + (orderselect.value + 1) + numstring);
117.                 startingCellAmountGroups[i] = new GameObject[int.Parse(GameObject.Find("NMP" + (orderselect.value + 1) + numstring).transform.Find("StartNumberInputField").transform.Find("Text").GetComponent<Text>().text)];
118.
119.                 for (int j = 1; j < cellNumber; ++j)
120.                 {
121.                     string secondNumstring = (j).ToString();
122.                     GameObject prob = basePage.transform.Find("ProbabilityScrollView").transform.Find("1stProbContent").gameObject;
123.
124.                     probs[((j + ((cellNumber - 1) * i)) - 1)] = Mathf.RoundToInt((float.Parse(prob.transform.Find("ProbInputField1." + secondNumstring).Find("ProbTransValue").GetComponent<Text>().text)) * 1000000000) * (int.MaxValue / 1000000000);
125.
126.                     string tempTransition = prob.transform.Find("ProbInputField1." + secondNumstring).transform.Find("ProbTransText").GetComponent<Text>().text.Remove(0, 33);
127.                     string tempTransition2 = tempTransition.Remove(1, 1);
128.                     int transitionNumber = int.Parse(tempTransition2);
129.                     cellTypeTransitions[((j + ((cellNumber - 1) * i)) - 1)] = transitionNumber;
130.                 }
131.             }
132.             //NOTE!!! The above sets a minimum probability value of 0.000000001. Changing to using floats might solve the problem.
133.         }
134.
135.         if (orderselect.value == 1)
136.         {
137.             if (neighborhoodType.value == 0)
138.             {
139.                 cellTypeTransitions = new int[((cellNumber - 1) * 5) * cellNumber];
140.                 neighborProbs = new int[((cellNumber - 1) * 5) * cellNumber];
141.                 for (int i = 0; i < cellNumber; ++i)
142.                 {
143.                     string numstring = (i + 1).ToString();
144.                     basePage = GameObject.Find("NMP2" + numstring);
145.                     probParent = basePage.transform.Find("ProbabilityScrollView").transform.Find("1stProbContent").gameObject;
146.                     startingCellAmountGroups[i] = new GameObject[int.Parse(GameObject.Find("NMP" + (orderselect.value + 1) + numstring).transform.Find("StartNumberInputField").transform.Find("Text").GetComponent<Text>().text)];
147.                     newColors[i] = GameObject.Find("NMP2" + numstring).transform.Find("ColorDropdown").GetComponent<Dropdown>().value;
148.                     for (int j = 0; j < (cellNumber - 1); ++j)
149.                     {
150.                         for (int k = 0; k < 5; ++k)
151.                         {
152.                             string secondNumString = (j + 1).ToString();
153.                             currentInputField = probParent.transform.Find("ProbInputField2." + secondNumString + "." + k.ToString()).gameObject;
154.                             inputFieldText = currentInputField.transform.Find("ProbTransValue").GetComponent<Text>().text;
155.                             neighborProbs[(i * ((cellNumber - 1) * 5)) + (j * 5) + k] = Mathf.RoundToInt((float.Parse(probParent.transform.Find("ProbInputField2." + secondNumString + "." + k.ToString()).transform.Find("ProbTransValue").GetComponent<Text>().text)) * 1000000000) * (int.MaxValue / 1000000000);
156.                             tempTransition = probParent.transform.Find("ProbInputField2." + secondNumString + "." + k).transform.Find("ProbTransText").GetComponent<Text>().text.Remove(0, 61);
157.                             tempTransition2 = tempTransition.Remove(1, 8);
158.                             int transitionNumber = int.Parse(tempTransition2);
159.                             cellTypeTransitions[(i * ((cellNumber - 1) * 5)) + (j * 5) + k] = transitionNumber;
160.                         }
161.                     }
162.                 }
163.             }
164.         }
165.
166.         //This dictionary only uses the Unity-named colors because it's easier to pick these in a drop-down than to have a color selector.
167.         //Future updates might adjust the UI page to allow for direct color selection, which could then be passed to this script and avoid the artificial limitations here.
168.         //After all, this can only show up to 9 cell types. Any number is possible, but with only 9 colors you can only differentiate between nine types.
169.         dict = new Dictionary<int, Color>();
179.
180.         inverseDict = new Dictionary<Color, int>();
190.
191.
192.         //UI stuff and grid formation
193.         {
194.             tilePrefab = GameObject.FindGameObjectWithTag("tilePrefab");
195.             gridWidth = int.Parse(GameObject.Find("Horigrid").GetComponent<Text>().text);
196.             gridHeight = int.Parse(GameObject.Find("Vertgrid").GetComponent<Text>().text);
197.             intGridWidth = Convert.ToInt32(Math.Ceiling(gridWidth));
198.             intGridHeight = Convert.ToInt32(Math.Ceiling(gridHeight));
199.
200.             float xOffset = 0.0f, yOffset = 0.0f;
201.             scaleHeight = ((Screen.height / (Screen.height / 10)) / gridHeight);
202.             scaleWidth = ((Screen.width / (Screen.width / 10)) / gridWidth);
203.             trueScale = Mathf.Min(scaleHeight, scaleWidth);
204.             tilePrefab.transform.localScale = new Vector3(trueScale, trueScale, trueScale);
205.
206.             gridOfCells = new GameObject[intGridWidth, intGridHeight];
207.
208.             for (xTilesCreated = 0; xTilesCreated < gridWidth; xTilesCreated += 1)
209.             {
210.                 for (yTilesCreated = 0; yTilesCreated < gridHeight; yTilesCreated += 1)
211.                 {
212.                     Vector3 position = new Vector3(transform.position.x + xOffset + (trueScale / 2), transform.position.y + yOffset + (trueScale / 2), transform.position.z);
213.
214.                     GameObject tile = Instantiate(tilePrefab, position, Quaternion.identity) as GameObject;
215.                     yOffset += trueScale + tileDistance;
216.                     tile.name = ("Cell" + ((xTilesCreated * gridWidth) + yTilesCreated));
217.                     tile.tag = ("Cells");
218.                     gridOfCells[xTilesCreated, yTilesCreated] = tile;
219.                 }
220.                 xOffset += trueScale + tileDistance;
221.                 yOffset = 0.0f;
222.             }
223.             tilePrefab.GetComponent<Renderer>().enabled = false;
224.             tiles = GameObject.FindGameObjectsWithTag("Cells");
225.             for (xTilesCreated = 0; xTilesCreated < gridWidth; xTilesCreated += 1)
226.             {
227.                 for (yTilesCreated = 0; yTilesCreated < gridHeight; yTilesCreated += 1)
228.                 {
229.                     SetNeighbor();
230.                     yield return new WaitForSeconds(0.5f);
231.                 }
232.             }
233.
234.             using (StreamWriter wt = File.AppendText(Application.persistentDataPath + "Run " + currentRun + ".txt"))
235.             {
236.                 wt.Write("Iteration: " + (currentIteration + 1));
237.                 for (int i = 0; i < neighborArray.Length; ++i)
238.                 {
239.                     try
240.                     {
241.                         wt.Write(neighborArray[i].name);
242.                     }
243.                     catch (System.NullReferenceException)
244.                     {
245.                         wt.Write("Empty");
246.                     }
247.
248.                     wt.WriteLine();
249.                 }
250.
251.                 wt.Close();
252.             }
253.
254.             foreach (GameObject tile in tiles)
255.             {
256.                 tile.transform.localPosition = new Vector3((tile.transform.position.x - (gridWidth / 2) * trueScale), (tile.transform.position.y - ((gridHeight / 2)) * trueScale), tile.transform.position.z);
257.                 tile.GetComponent<Renderer>().enabled = true;
258.             }
259.
260.         }
261.
262.         //gridCheck = gridOfCells[1, 0];
263.
264.         //This sets up the probability array to have the appropriate values for probability ranges
265.         if (orderselect.value == 0)
266.         {
267.             for (int i = 0; i < (cellNumber * (cellNumber - 1)); i += (cellNumber - 1))
268.             {
269.                 for (int j = (i + (cellNumber - 2)); j > (i - 1); --j)
270.                 {
271.                     for (int k = (j - 1); k > (i - 1); --k)
272.                     {
273.                         probs[j] = probs[j] + probs[k];
274.                     }
275.                 }
276.
277.             }
278.         }
279.
280.         //This is the actual iteration loop
281.         for (currentRun = 1; currentRun < (maxRuns + 1); ++currentRun)
282.         {
283.             //This first shuffles the group of objects, then changes the colors of the objects based on cell type starting amounts
284.             tiles.Shuffle();
285.             for (int i = 0; i < cellNumber; ++i)
286.             {
287.                 arrayCopyRangeStart = 0;
288.                 int j = 0;
289.                 for (int k = (i - 1); k > (-1); --k)
290.                 {
291.                     arrayCopyRangeStart = j + startingCellAmountGroups[k].Length;
292.                     j = arrayCopyRangeStart;
293.                 }
294.                 Array.Copy(tiles, arrayCopyRangeStart, startingCellAmountGroups[i], 0, startingCellAmountGroups[i].Length);
295.                 foreach (GameObject tile in startingCellAmountGroups[i])
296.                 {
297.                     tile.GetComponent<Renderer>().material.color = dict[newColors[i]];
298.                     cellType = (i + 1);
299.                 }
300.             }
301.
302.             for (currentIteration = 0; currentIteration < (maxIterations); currentIteration++)
303.             {
304.                 tiles.Shuffle();
305.                 for (int k = 0; k < cellNumber; ++k) cellNumberCounts[k] = 0;
306.                 foreach (GameObject tile in tiles)
307.                 {
308.                     if (orderselect.value == 1)
309.                     {
310.                         cellType = (Array.IndexOf(newColors, inverseDict[tile.GetComponent<Renderer>().material.color])) + 1;
311.                         currentCellNumber = int.Parse(tile.name.Remove(0, 4));
312.                         CheckNeighbors();
313.                         organizeProbs();
314.                     }
315.
316.                     result = UnityEngine.Random.Range(0, int.MaxValue);
317.                     testNumber1 = result;
318.                         int i = ((cellType - 1) * (cellNumber - 1));
319.                     if (orderselect.value == 1)
320.                         {
321.                             for (int j = (i + (cellNumber - 2)); j > i; --j)
322.                             {
323.                                 testNumber2 = probs[j];
324.                                 testNumber4 = probs[i];
325.                                 if (result < probs[j] && result >= probs[j - 1])
326.                                 {
327.                                     tile.GetComponent<Renderer>().material.color = dict[newColors[(cellTypeTransitions[((i * 5) + ((j - i) * 5))] - 1)]];
328.                                     cellType = cellTypeTransitions[((i * 5) + ((j - i) * 5))];
329.                                     goto Label;
330.                                 }
331.                             }
332.                             if (result < probs[i])
333.                             {
334.                                 tile.GetComponent<Renderer>().material.color = dict[newColors[(cellTypeTransitions[(i * 5)] - 1)]];
335.                                 cellType = cellTypeTransitions[(i * 5)];
336.                                 goto Label;
337.                             }
338.                         }
339.                     if (orderselect.value == 0)
340.                             {
341.                                 for (int j = (i + (cellNumber - 2)); j > i; --j)
342.                                 {
343.                                     testNumber2 = probs[j];
344.                                     testNumber4 = probs[i];
345.                                     if (result < probs[j] && result >= probs[j - 1])
346.                                     {
347.                                         tile.GetComponent<Renderer>().material.color = dict[newColors[(cellTypeTransitions[j] - 1)]];
348.                                         cellType = cellTypeTransitions[j];
349.                                         goto Label;
350.                                     }
351.                                 }
352.                                 if (result < probs[i])
353.                                 {
354.                                     tile.GetComponent<Renderer>().material.color = dict[newColors[(cellTypeTransitions[i] - 1)]];
355.                                     cellType = cellTypeTransitions[i];
356.                                     goto Label;
357.                                 }
358.                             }
359.                 Label:
360.                     ;
361.
362.                     cellNumberCounts[(cellType - 1)] += 1;
363.                 }
364.
365.                 if (saveFileCheck.isOn == true)
366.                 {
367.                     using (StreamWriter wt = File.AppendText(Application.persistentDataPath + "Run " + currentRun + ".txt"))
368.                     {
369.                         wt.Write("Iteration: " + (currentIteration + 1));
370.                         for (int i = 0; i < cellNumber; ++i)
371.                         {
372.                             string cellTypeString = (i + 1).ToString();
373.                             wt.Write(" Cell Type " + cellTypeString + ": " + cellNumberCounts[i]);
374.                         }
375.                         wt.WriteLine();
376.                         wt.Close();
377.                     }
378.                 }
379.                     IterationText.GetComponent<Text>().text = "Iteration: " + (currentIteration + 1).ToString();
380.                     RunText.GetComponent<Text>().text = "Run: " + currentRun.ToString();
381.                     yield return new WaitForSeconds(timeToWait);
382.                 }
383.         }
384.     }
385.
386.     void organizeProbs()
387.     {
388.         for (int i = 0; i < (cellNumber * (cellNumber - 1)); i += (cellNumber - 1))
389.         {
390.             for (int j = (i + (cellNumber - 2)); j > (i - 1); --j)
391.             {
392.                 for (int k = (j - 1); k > (i - 1); --k)
393.                 {
394.                     probs[j] = probs[j] + probs[k];
395.                 }
396.             }
397.
398.         }
399.     }
400.
401.     void SetNeighbor()
402.     {
403.         if (orderselect.value != 0)
404.         {
405.             // if (gridType.value == 0)
406.             if (neighborhoodType.value == 0)
407.             {
408.                 neighborArray = new GameObject[(intGridHeight * intGridWidth) * 4];
409.                 try
410.                 {
411.                     neighborUp = gridOfCells[(xTilesCreated), (yTilesCreated + 1)];
412.                 }
413.                 catch (System.IndexOutOfRangeException)
414.                 {
415.                     neighborUp = null;
416.                 }
417.                 try
418.                 {
419.                     neighborDown = gridOfCells[(xTilesCreated), (yTilesCreated - 1)];
420.                 }
421.                 catch (System.IndexOutOfRangeException)
422.                 {
423.                     neighborDown = null;
424.                 }
425.                 try
426.                 {
427.                     neighborLeft = gridOfCells[(xTilesCreated - 1), (yTilesCreated)];
428.                 }
429.                 catch (System.IndexOutOfRangeException)
430.                 {
431.                     neighborLeft = null;
432.                 }
433.                 try
434.                 {
435.                     neighborRight = gridOfCells[(xTilesCreated + 1), (yTilesCreated)];
436.                 }
437.                 catch (System.IndexOutOfRangeException)
438.                 {
439.                     neighborRight = null;
440.                 }
441.                 neighborArray[((((xTilesCreated * intGridWidth) + yTilesCreated) * 4) + 0)] = neighborUp;
442.                 neighborArray[((((xTilesCreated * intGridWidth) + yTilesCreated) * 4) + 1)] = neighborDown;
443.                 neighborArray[((((xTilesCreated * intGridWidth) + yTilesCreated) * 4) + 2)] = neighborLeft;
444.                 neighborArray[((((xTilesCreated * intGridWidth) + yTilesCreated) * 4) + 3)] = neighborRight;
445.                 testGameObject = neighborArray[((((xTilesCreated * intGridWidth) + yTilesCreated) * 4) + 0)];
446.
447.             }
448.         }
449.         else
450.         {
451.         }
452.     }
453.
454.     void CheckNeighbors()
455.     {
456.         upColor = Color.clear;
457.         downColor = Color.clear;
458.         leftColor = Color.clear;
459.         rightColor = Color.clear;
460.         testNumber5 = (currentCellNumber * 4);
461.         try
462.         {
463.             upColor = neighborArray[((currentCellNumber * 4) + 0)].GetComponent<Renderer>().material.color;
464.             NewneighborUp = neighborArray[((currentCellNumber * 4) + 0)];
465.             neighborUpColor = upColor;
466.         }
467.         catch (System.NullReferenceException)
468.         {
469.         }
470.         try
471.         {
472.             downColor = neighborArray[((currentCellNumber * 4) + 1)].GetComponent<Renderer>().material.color;
473.             NewneighborDown = neighborArray[((currentCellNumber * 4) + 1)];
474.             neighborDownColor = downColor;
475.         }
476.         catch (System.NullReferenceException)
477.         {
478.         }
479.         try
480.         {
481.             leftColor = neighborArray[((currentCellNumber * 4) + 2)].GetComponent<Renderer>().material.color;
482.             NewneighborLeft = neighborArray[((currentCellNumber * 4) + 2)];
483.             neighborLeftColor = leftColor;
484.         }
485.         catch (System.NullReferenceException)
486.         {
487.         }
488.         try
489.         {
490.             rightColor = neighborArray[((currentCellNumber * 4) + 3)].GetComponent<Renderer>().material.color;
491.             NewneighborRight = neighborArray[((currentCellNumber * 4) + 3)];
492.             neighborRightColor = rightColor;
493.         }
494.         catch (System.NullReferenceException)
495.         {
496.         }
497.         Color[] vonNeumannGroup = new Color[4] { upColor, downColor, leftColor, rightColor };
498.         List<Color> filteredVonNeumann = new List<Color>();
499.         for (int currentCheck = 0; currentCheck < cellNumber; ++currentCheck)
500.         {
501.             int tempValue = 0;
502.             if (currentCheck == (cellType - 1)) continue;
503.             if (currentCheck < (cellType - 1))
504.             {
505.                 for (int j = 0; j < 4; ++j)
506.                 {
507.
508.                     if (vonNeumannGroup[j] == dict[newColors[currentCheck]])
509.                     {
511.                     }
512.                 }
513.                 testNumber3 = filteredVonNeumann.Count;
514.                 if (filteredVonNeumann.Count == 4)
515.                 {
516.                     tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 4];
517.                 }
518.                 if (filteredVonNeumann.Count == 3)
519.                 {
520.                     tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 3];
521.                 }
522.                 if (filteredVonNeumann.Count == 2)
523.                 {
524.                     tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 2];
525.                 }
526.                 if (filteredVonNeumann.Count == 1)
527.                 {
528.                     tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 1];
529.                 }
530.                 if (filteredVonNeumann.Count == 0)
531.                 {
532.                     tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5))];
533.                 }
534.                 probs[((cellType - 1) * (cellNumber - 1)) + currentCheck] = tempValue;
535.             }
536.             else if (currentCheck > (cellType - 1))
537.             {
538.                 for (int j = 0; j < 4; ++j)
539.                 {
540.
541.                     if (vonNeumannGroup[j] == dict[newColors[currentCheck]])
542.                     {
544.                     }
545.                 }
546.                 if (filteredVonNeumann.Count == 4)
547.                 {
548.                     tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 4];
549.                 }
550.                 if (filteredVonNeumann.Count == 3)
551.                 {
552.                     tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 3];
553.                 }
554.                 if (filteredVonNeumann.Count == 2)
555.                 {
556.                     tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 2];
557.                 }
558.                 if (filteredVonNeumann.Count == 1)
559.                 {
560.                     tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 1];
561.                 }
562.                 if (filteredVonNeumann.Count == 0)
563.                 {
564.                     tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5))];
565.                 }
566.                 probs[((cellType - 1) * (cellNumber - 1)) + (currentCheck - 1)] = tempValue;
567.             }
568.
569.         }
570.     }
571. }
572.
574. {
576.     private static System.Random Local;
577.
579.     {
581.     }
582. }
583.
584. static class MyExtensions
585. {
586.     public static void Shuffle<T>(this IList<T> list)
587.     {
588.         int n = list.Count;
589.         while (n > 1)
590.         {
591.             n--;