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:
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using UnityEngine;
using UnityEngine.UI;
public class GridMake : MonoBehaviour
{
GameObject tilePrefab;
public float gridWidth, gridHeight;
GameObject[] tiles;
GameObject[,] gridOfCells;
public GameObject gridCheck;
public float time = 0.0f;
public float timeToWait = 0f;
public int cellType;
public int cellNumber;
int maxIterations = 0;
int currentIteration;
public int arrayCopyRangeStart;
int result;
public float trueScale;
public float scaleHeight;
public float scaleWidth;
int maxRuns = 0;
int currentRun;
GameObject IterationText;
GameObject RunText;
public Toggle saveFileCheck;
Dropdown neighborhoodType;
Dictionary<int, Color> dict;
int[] newColors;
Dropdown orderselect;
int[] probs;
int[] neighborProbs;
int[] cellTypeTransitions;
int xTilesCreated, yTilesCreated;
public GameObject neighborUp;
public GameObject neighborDown;
public GameObject neighborLeft;
public GameObject neighborRight;
Color currentColor;
GameObject basePage;
GameObject probParent;
GameObject currentInputField;
public string inputFieldText;
public int testNumber1;
public int testNumber2 = 0;
public int testNumber3;
public int testNumber4;
public string tempTransition;
public string tempTransition2;
public float tileDistance = 0.0f; //Spacing between tiles
void Start()
{
}
void Update()
{
}
public IEnumerator CreateTiles()
{
orderselect = GameObject.FindGameObjectWithTag("OrderDropDown").GetComponent<Dropdown>();
cellNumber = int.Parse(GameObject.FindGameObjectWithTag("numbercelltype").GetComponent<Text>().text);
maxIterations = int.Parse(GameObject.Find("IterationCount").GetComponent<Text>().text);
maxRuns = int.Parse(GameObject.Find("RunCount").GetComponent<Text>().text);
timeToWait = float.Parse(GameObject.Find("WaitTimeCount").GetComponent<Text>().text);
IterationText = GameObject.Find("IterationText");
RunText = GameObject.Find("RunText");
newColors = new int[cellNumber];
probs = new int[(cellNumber) * (cellNumber - 1)];
int[] cellNumberCounts = new int[cellNumber];
Array[] startingCellAmountGroups = new Array[cellNumber];
neighborhoodType = GameObject.FindGameObjectWithTag("NeighborhoodDropdown").GetComponent<Dropdown>();
if (orderselect.value == 0)
{
cellTypeTransitions = new int[(cellNumber) * (cellNumber - 1)];
for (int i = 0; i < cellNumber; ++i)
{
string numstring = (i + 1).ToString();
newColors[i] = GameObject.Find("NMP" + (orderselect.value + 1) + numstring).transform.Find("ColorDropdown").GetComponent<Dropdown>().value;
GameObject basePage = GameObject.Find("NMP" + (orderselect.value + 1) + numstring);
startingCellAmountGroups[i] = new GameObject[int.Parse(GameObject.Find("NMP" + (orderselect.value + 1) + numstring).transform.Find("StartNumberInputField").transform.Find("Text").GetComponent<Text>().text)];
for (int j = 1; j < cellNumber; ++j)
{
string secondNumstring = (j).ToString();
GameObject prob = basePage.transform.Find("ProbabilityScrollView").transform.Find("1stProbContent").gameObject;
probs[((j + ((cellNumber - 1) * i)) - 1)] = Mathf.RoundToInt((float.Parse(prob.transform.Find("ProbInputField1." + secondNumstring).Find("ProbTransValue").GetComponent<Text>().text)) * 1000000000) * (int.MaxValue / 1000000000);
string tempTransition = prob.transform.Find("ProbInputField1." + secondNumstring).transform.Find("ProbTransText").GetComponent<Text>().text.Remove(0, 33);
string tempTransition2 = tempTransition.Remove(1, 1);
int transitionNumber = int.Parse(tempTransition2);
cellTypeTransitions[((j + ((cellNumber - 1) * i)) - 1)] = transitionNumber;
}
}
//NOTE!!! The above sets a minimum probability value of 0.000000001. Changing to using floats might solve the problem.
}
if (orderselect.value == 1)
{
if (neighborhoodType.value == 0)
{
cellTypeTransitions = new int[((cellNumber - 1) * 5) * cellNumber];
neighborProbs = new int[((cellNumber - 1) * 5) * cellNumber];
for (int i = 0; i < cellNumber; ++i)
{
string numstring = (i + 1).ToString();
basePage = GameObject.Find("NMP2" + numstring);
probParent = basePage.transform.Find("ProbabilityScrollView").transform.Find("1stProbContent").gameObject;
startingCellAmountGroups[i] = new GameObject[int.Parse(GameObject.Find("NMP" + (orderselect.value + 1) + numstring).transform.Find("StartNumberInputField").transform.Find("Text").GetComponent<Text>().text)];
newColors[i] = GameObject.Find("NMP2" + numstring).transform.Find("ColorDropdown").GetComponent<Dropdown>().value;
for (int j = 0; j < (cellNumber - 1); ++j)
{
for (int k = 0; k < 5; ++k)
{
string secondNumString = (j + 1).ToString();
currentInputField = probParent.transform.Find("ProbInputField2." + secondNumString + "." + k.ToString()).gameObject;
inputFieldText = currentInputField.transform.Find("ProbTransValue").GetComponent<Text>().text;
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);
tempTransition = probParent.transform.Find("ProbInputField2." + secondNumString + "." + k).transform.Find("ProbTransText").GetComponent<Text>().text.Remove(0, 61);
tempTransition2 = tempTransition.Remove(1, 8);
int transitionNumber = int.Parse(tempTransition2);
cellTypeTransitions[(i * ((cellNumber - 1) * 5)) + (j * 5) + k] = transitionNumber;
}
}
}
}
}
//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.
//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.
//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.
dict = new Dictionary<int, Color>();
dict.Add(0, Color.black);
dict.Add(1, Color.blue);
dict.Add(2, Color.cyan);
dict.Add(3, Color.gray);
dict.Add(4, Color.green);
dict.Add(5, Color.magenta);
dict.Add(6, Color.red);
dict.Add(7, Color.white);
dict.Add(8, Color.yellow);
//UI stuff and grid formation
{
tilePrefab = GameObject.FindGameObjectWithTag("tilePrefab");
gridWidth = int.Parse(GameObject.Find("Horigrid").GetComponent<Text>().text);
gridHeight = int.Parse(GameObject.Find("Vertgrid").GetComponent<Text>().text);
float xOffset = 0.0f, yOffset = 0.0f;
scaleHeight = ((Screen.height / (Screen.height / 10)) / gridHeight);
scaleWidth = ((Screen.width / (Screen.width / 10)) / gridWidth);
trueScale = Mathf.Min(scaleHeight, scaleWidth);
tilePrefab.transform.localScale = new Vector3(trueScale, trueScale, trueScale);
gridOfCells = new GameObject[Convert.ToInt32(Math.Ceiling(gridWidth)), Convert.ToInt32(Math.Ceiling(gridHeight))];
for (xTilesCreated = 0; xTilesCreated < gridWidth; xTilesCreated += 1)
{
for (yTilesCreated = 0; yTilesCreated < gridHeight; yTilesCreated += 1)
{
Vector3 position = new Vector3(transform.position.x + xOffset + (trueScale / 2), transform.position.y + yOffset + (trueScale / 2), transform.position.z);
GameObject tile = Instantiate(tilePrefab, position, Quaternion.identity) as GameObject;
yOffset += trueScale + tileDistance;
tile.name = ("Cell" + ((xTilesCreated * gridWidth) + yTilesCreated));
tile.tag = ("Cells");
gridOfCells[xTilesCreated, yTilesCreated] = tile;
SetNeighbor();
}
xOffset += trueScale + tileDistance;
yOffset = 0.0f;
}
tilePrefab.GetComponent<Renderer>().enabled = false;
tiles = GameObject.FindGameObjectsWithTag("Cells");
foreach (GameObject tile in tiles)
{
tile.transform.localPosition = new Vector3((tile.transform.position.x - (gridWidth / 2) * trueScale), (tile.transform.position.y - ((gridHeight / 2)) * trueScale), tile.transform.position.z);
tile.GetComponent<Renderer>().enabled = true;
}
}
gridCheck = gridOfCells[1, 0];
//This sets up the probability array to have the appropriate values for probability ranges
if (orderselect.value == 0)
{
for (int i = 0; i < (cellNumber * (cellNumber - 1)); i += (cellNumber - 1))
{
for (int j = (i + (cellNumber - 2)); j > (i - 1); --j)
{
for (int k = (j - 1); k > (i - 1); --k)
{
probs[j] = probs[j] + probs[k];
}
}
}
}
//This is the actual iteration loop
for (currentRun = 1; currentRun < (maxRuns + 1); ++currentRun)
{
//This first shuffles the group of objects, then changes the colors of the objects based on cell type starting amounts
tiles.Shuffle();
for (int i = 0; i < cellNumber; ++i)
{
arrayCopyRangeStart = 0;
int j = 0;
for (int k = (i - 1); k > (-1); --k)
{
arrayCopyRangeStart = j + startingCellAmountGroups[k].Length;
j = arrayCopyRangeStart;
}
Array.Copy(tiles, arrayCopyRangeStart, startingCellAmountGroups[i], 0, startingCellAmountGroups[i].Length);
foreach (GameObject tile in startingCellAmountGroups[i])
{
tile.GetComponent<Renderer>().material.color = dict[newColors[i]];
cellType = (i + 1);
}
}
for (currentIteration = 0; currentIteration < (maxIterations); currentIteration++)
{
tiles.Shuffle();
for (int k = 0; k < cellNumber; ++k) cellNumberCounts[k] = 0;
foreach (GameObject tile in tiles)
{
if (orderselect.value == 1)
{
CheckNeighbors();
//organizeProbs();
testNumber1 = probs[1];
}
result = UnityEngine.Random.Range(0, int.MaxValue);
int i = ((cellType - 1) * (cellNumber - 1));
{
for (int j = (i + (cellNumber - 2)); j > i; --j)
{
if (result <= probs[j] && result > probs[j - 1])
{
tile.GetComponent<Renderer>().material.color = dict[newColors[(cellTypeTransitions[j] - 1)]];
cellType = cellTypeTransitions[j];
goto Label;
}
}
if (result <= probs[i])
{
tile.GetComponent<Renderer>().material.color = dict[newColors[(cellTypeTransitions[i] - 1)]];
cellType = cellTypeTransitions[i];
goto Label;
}
}
Label:
;
cellNumberCounts[(cellType - 1)] += 1;
}
if (saveFileCheck.isOn == true)
{
using (StreamWriter wt = File.AppendText(Application.persistentDataPath + "Run " + currentRun + ".txt"))
{
wt.Write("Iteration: " + (currentIteration + 1));
for (int i = 0; i < cellNumber; ++i)
{
string cellTypeString = (i + 1).ToString();
wt.Write(" Cell Type " + cellTypeString + ": " + cellNumberCounts[i]);
}
wt.WriteLine();
wt.Close();
}
}
IterationText.GetComponent<Text>().text = "Iteration: " + (currentIteration + 1).ToString();
RunText.GetComponent<Text>().text = "Run: " + currentRun.ToString();
yield return new WaitForSeconds(timeToWait);
}
}
}
void organizeProbs()
{
for (int i = 0; i < (cellNumber * (cellNumber - 1)); i += (cellNumber - 1))
{
for (int j = (i + (cellNumber - 2)); j > (i - 1); --j)
{
for (int k = (j - 1); k > (i - 1); --k)
{
probs[j] = probs[j] + probs[k];
}
}
}
}
void SetNeighbor()
{
if (orderselect.value != 0)
{
// if (gridType.value == 0)
if (neighborhoodType.value == 0)
{
try
{
neighborUp = gridOfCells[(xTilesCreated), (yTilesCreated + 1)];
}
catch (System.IndexOutOfRangeException)
{
}
try
{
neighborDown = gridOfCells[(xTilesCreated), (yTilesCreated - 1)];
}
catch (System.IndexOutOfRangeException)
{
}
try
{
neighborLeft = gridOfCells[(xTilesCreated - 1), (yTilesCreated)];
}
catch (System.IndexOutOfRangeException)
{
}
try
{
neighborRight = gridOfCells[(xTilesCreated + 1), (yTilesCreated)];
}
catch (System.IndexOutOfRangeException)
{
}
}
}
else
{
}
}
void CheckNeighbors()
{
Color upColor = Color.clear;
Color downColor = Color.clear;
Color leftColor = Color.clear;
Color rightColor = Color.clear;
try
{
upColor = neighborUp.GetComponent<Renderer>().material.color;
}
catch (System.NullReferenceException)
{
}
try
{
downColor = neighborDown.GetComponent<Renderer>().material.color;
}
catch (System.NullReferenceException)
{
}
try
{
leftColor = neighborLeft.GetComponent<Renderer>().material.color;
}
catch (System.NullReferenceException)
{
}
try
{
rightColor = neighborRight.GetComponent<Renderer>().material.color;
}
catch (System.NullReferenceException)
{
}
Color[] vonNeumannGroup = new Color[4] { upColor, downColor, leftColor, rightColor };
List<Color> filteredVonNeumann = new List<Color>();
for (int currentCheck = 0; currentCheck < cellNumber; ++currentCheck)
{
int tempValue = 0;
if (currentCheck == (cellType - 1)) continue;
if (currentCheck < (cellType - 1))
{
for (int j = 0; j < 4; ++j)
{
if (vonNeumannGroup[j] == dict[newColors[currentCheck]])
{
filteredVonNeumann.Add(vonNeumannGroup[j]);
}
}
if (filteredVonNeumann.Count == 4)
{
tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 4];
}
if (filteredVonNeumann.Count == 3)
{
tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 3];
}
if (filteredVonNeumann.Count == 2)
{
tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 2];
}
if (filteredVonNeumann.Count == 1)
{
tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5)) + 1];
}
if (filteredVonNeumann.Count == 0)
{
tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + (currentCheck * 5))];
}
probs[((cellType - 1) * (cellNumber - 1)) + currentCheck] = tempValue;
}
if (currentCheck > (cellType - 1))
{
for (int j = 0; j < 4; ++j)
{
if (vonNeumannGroup[j] == dict[newColors[currentCheck]])
{
filteredVonNeumann.Add(vonNeumannGroup[j]);
}
}
if (filteredVonNeumann.Count == 4)
{
tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 4];
}
if (filteredVonNeumann.Count == 3)
{
tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 3];
}
if (filteredVonNeumann.Count == 2)
{
tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 2];
}
if (filteredVonNeumann.Count == 1)
{
tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5)) + 1];
}
if (filteredVonNeumann.Count == 0)
{
tempValue = neighborProbs[(((cellType - 1) * ((cellNumber - 1) * 5)) + ((currentCheck - 1) * 5))];
}
probs[((cellType - 1) * (cellNumber - 1)) + (currentCheck - 1)] = tempValue;
}
testNumber4 = probs[((cellType - 1) * (cellNumber - 1)) + (currentCheck - 1)];
testNumber3 = probs.Length;
}
}
}
public static class ThreadSafeRandom
{
[ThreadStatic]
private static System.Random Local;
public static System.Random ThisThreadsRandom
{
get { return Local ?? (Local = new System.Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); }
}
}
static class MyExtensions
{
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
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.
2952870–219021–Cellular Automata.7z (2.81 MB)