Search Unity

  1. Calling all beginners! Join the FPS Beginners Mods Challenge until December 13.
    Dismiss Notice
  2. It's Cyber Week at the Asset Store!
    Dismiss Notice

Making a Complex Grid

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

  1. EternalAmbiguity

    EternalAmbiguity

    Joined:
    Dec 27, 2014
    Posts:
    2,142
    Okay, I think I see. I'm familiar with the broad ideas, but the specifics are new to me. Thanks for explaining.

    And I tried changing it to what you have, and it works perfectly. I really can't thank you enough. My professor has congratulated me after seeing it in action (though it didn't have these later features), but I know it wasn't simply me that did all this. Thanks again.

    And with that...I think stage 2 is somewhat complete. I want to go back and get the CA without any neighbor effects working (what I initially made with the GridMake script). Then I want to get the Moore neighborhood working (which should be simple I imagine--just changing a few things here and there). After that I think I'll try to get the UI stuff cleaned up--everything is scaled to the resolution of the machine I started the project on, and I need to fix that, along with removing old things and changing text, etc.

    After all of that, however, the next and possibly final stage is to replicate his most advanced set of CA models, where the agents can actually move throughout the grid. They have a "random walk" ability (probability based of course). Additionally, there can be gravitational effects, or simply a "pull" on the random walk probability (I imagine it could be in any direction). Additionally, there's a breaking/joining effect where agents can clump together if they collide.

    My professor actually used this to model the surface of a liquid--which is not simply a liquid, but is actually little molecules entering and leaving the gas phase constantly (unfortunately that's not free to view, but if you're interested in seeing the paper just PM me). Relevant picture:

    CA Image.png

    Anyway, that's the next big stage.

    A few questions: one thing my professor has talked to me about is the possibility of having a "prebuilt" grid, or one that's custom-built. Imagine a large circular "object" placed in the middle of the grid. Is something like this possible? It's not something I would want to work on anytime soon, but if it's possible I want to go for it eventually.

    Second: is it possible to make the application multi-threaded? Most things are sequential or linear, but the iterative process of checking each individual cell per iteration could probably be split between threads. I could stand to have the current way I do things sped up, and it would be nice to be able to do larger sizes (I use 100 x 100 mainly, but 1000 x 1000 would be nice) as well.

    Third: do you have any idea how I might go about setting this up to read the settings from a file? I've got it all set up for the user to input everything (somewhat tediously, since there's no way to properly tab through things), but if the user could just have a file they modify and then read from that to get all of the necessary information it would probably save a bit of time and decrease the chance of errors re-entering information.

    None of these are a big concern. I would personally rate their importance as 2>3>1, though as things stand I don't need any of them right now.

    In any case I'm going to return to the initial CA type and get that working, as well as the Moore neighborhood.
     
  2. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    I assume this means don't worry about how many neighbors we have. Each cell has a chance to spontaneously change to a different state? That is pretty easy we just add a new Neighborhood to the neighborhood class
    Code (CSharp):
    1. public enum NType
    2. {
    3.     VonNeumann,
    4.     Moore,
    5.     None
    6. }
    Code (CSharp):
    1.  public int GetNeighborSize()
    2.     {
    3.         switch (neighborType)
    4.         {
    5.             case NType.Moore:
    6.                 return 8;
    7.             case NType.VonNeumann:
    8.                 return 4;
    9.             case NType.None:
    10.                 return 0;
    11.             default:
    12.                 throw new ArgumentException("Unknown Neighborhood type in GetNeighborSize");
    13.         }
    14.     }
    Code (CSharp):
    1.  public List<Point> GetNeighbors(Point cell)
    2.     {
    3.         switch (neighborType)
    4.         {
    5.             case NType.Moore:
    6.                 return GetMooreNeighbors(cell);
    7.             case NType.VonNeumann:
    8.                 return GetVonNeumannNeighbors(cell);
    9.             case NType.None:
    10.                 // just return an empyt list of points
    11.                 return new List<Point>();
    12.             default:
    13.                 throw new ArgumentException("Unknown Neighborhood type in GetNeighbors");
    14.         }
    15.     }
    As far as your three questions:
    1) You can easily have shapes added to your grid. You'd probably want to design this into a system that also allows the user to click on the CA grid itself while its paused to toggle through the states a cell is in. You could easily have a drop down somehwhere with some predetermined shapes (circle, square, etc). Maybe a slider for Radius. Then once they clicked that circle on the drop down they click on the CA and it sets all the cells in a radius around the point clicked. Its fairly simple algorithms to find all the points of a circle around a center given a radius.

    2) Yes, and one of the main advantages of splitting off our model, is that most of Unity isn't thread safe. But our CA is purely data manipulation. You can easily have it run its Iteration of a large grid in multiple threads. You can either have some bool Working set to true when it starts. Then the controller script after it calls OneIteration can keep checking In Update if the CA is still working, and when its finally done update the grid.

    3) Yes you just create some class that encapsulates all the data that one Setup should be. Then use some serialization technique (binary,xml,json) to write that class out to a file. It should be pretty easy to setup a new project and test writing and reading data out to a file to get use to how it works. .Net has a built in Binary Serializer.

    I hate to bring this up after you announced stage 2 finished, but there is still a major bug in your code involving creating the UI pages. You can see it if you create 3 states, start setting their information, then back out to the MainPage again. Even if you don't change the number of states, when you Hit next to go back into the state Pages Unity locks up. I was thinking about it and I think there is a much better way to handle the UI that won't involve too much refactoring. Its would work along the same lines as we did separating out the CA from the UI. This type we'll separate out the UI from the controller script using some data classes to pass information back and forth. (additionally it won't require prefabs or Instantiating a whole slew of new pages for each state). I'll post some code for it later. I'll add code to tab between fields as well. That was annoying testing things.
     
    EternalAmbiguity likes this.
  3. EternalAmbiguity

    EternalAmbiguity

    Joined:
    Dec 27, 2014
    Posts:
    2,142
    You know, I noticed that myself when I was testing on my own. Didn't think too much about it. But if you think there's a better way to do it, then by all means, show me. And thanks again, thanks a bunch.

    Edit: Okay, I had a massive wall of text here that was basically me running into problems and figuring them out. I'm editing this to remove all that and just put my end results.

    --added extended von Neumann.
    --added a list of images (per iteration), which the user can save to a new folder. I will make this toggle-able in the UI later.
    --removed the " * 10" stuff with the textures. Old ones are 45 times larger than the new ones in some cases (not just a disk space concern, but also a memory concern if I'm holding all of them in memory while the CA runs).
    --made the CA controller script and the Buttons scripts instantiation of UI pages universal. I know you're going to refactor the UI stuff, and I look forward to it, but I wanted this for now at least to make it simpler.

    I'm not seeing that bug you mentioned (and I saw before myself) anymore, not sure why.

    The one thing about all of this I could think about working on is the UI, but since you said you're going to post something for that I'll wait. I suppose I could work on serialization and make a load/save file, but that seems kind of difficult to even wrap my head around (haven't been able to for my games either).

    So I'm going to look at the multi-threading. Apparently .NET has stuff called "tasks" which are a bit higher-level, but control the threads. Not sure if that's the way to go or not. I'll look at it.

    Edit: apparently Unity uses really old .NET, and thus doesn't have access to Tasks. So perhaps a ThreadPool is the way to go. Though I'm unsure exactly how to set up a WaitCallback--when I try to point it to a regular method it doesn't work.

    But here's the new Buttons script, controller script, and a modified Neighborhood script, with all of the improvements.

    Code (csharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using UnityEngine.SceneManagement;
    4. using System.Collections;
    5.  
    6. public class Buttons : MonoBehaviour {
    7.     CanvasGroup[] panels;
    8.     CanvasGroup[] modelScenePanels;
    9.     public GameObject mmp;
    10.     public GameObject nmp;
    11.     public Dropdown neighborEffectsDropdown;
    12.     public Text numbercelltype;
    13.     public GameObject nmpcell;
    14.     public GameObject probinputfield;
    15.     Text probvalue;
    16.     Text probtitle;
    17.     GameObject[] celltitles;
    18.     GameObject[] probtitles;
    19.     public GameObject nmpnow;
    20.     GameObject confirm;
    21.     GameObject designate;
    22.     public int pageCounter = 10;
    23.     Text NMP1pageTitle;
    24.     Text NMP1probTitle;
    25.     public int pageMax;
    26.     public int pageMin;
    27.     bool applyActive = false;
    28.     GameObject mainCanvas;
    29.     public Text pauseButtonText;
    30.     public bool continueCA = false;
    31.  
    32.     public float countDiff;
    33.  
    34.     GameObject gridpanel;
    35.     GameObject[] cellpages;
    36.     GameObject[] inputfields;
    37.  
    38.     public int surroundingCells;
    39.     public int toProbCell;
    40.     // Use this for initialization
    41.     void Start ()
    42.     {
    43.         neighborEffectsDropdown = GameObject.FindGameObjectWithTag("NeighborhoodDropdown").GetComponent<Dropdown>();
    44.         probinputfield = GameObject.FindGameObjectWithTag("ProbInputField");
    45.         pageMin = 11;
    46.         panels = GameObject.Find("Canvas").GetComponentsInChildren<CanvasGroup>();
    47.         foreach (CanvasGroup panel in panels)
    48.         {
    49.             panel.alpha = 0;
    50.             panel.interactable = false;
    51.             panel.blocksRaycasts = false;
    52.         }
    53.         gridpanel = GameObject.Find("GridPanel");
    54.         {
    55.             gridpanel.GetComponent<CanvasGroup>().alpha = 0;
    56.             gridpanel.GetComponent<CanvasGroup>().interactable = false;
    57.             gridpanel.GetComponent<CanvasGroup>().blocksRaycasts = false;
    58.         }
    59.         mmp = GameObject.FindGameObjectWithTag("MainMenuPanel");
    60.         mmp.GetComponent<CanvasGroup>().alpha = 1;
    61.         mmp.GetComponent<CanvasGroup>().interactable = true;
    62.         mmp.GetComponent<CanvasGroup>().blocksRaycasts = true;
    63.     }
    64.  
    65.     public void PageCount ()
    66.     {
    67.         if(applyActive == true)
    68.         {
    69.             GameObject[] inputFields = GameObject.FindGameObjectsWithTag("InstantiatedInputFields");
    70.             GameObject[] cellPages = GameObject.FindGameObjectsWithTag("InstantiatedCellPages");
    71.             foreach (GameObject inputField in inputFields)
    72.             {
    73.                 GameObject.Destroy(inputField);
    74.             }
    75.             foreach (GameObject cellPage in cellPages)
    76.             {
    77.                 GameObject.Destroy(cellPage);
    78.             }
    79.         }
    80.        
    81.             pageCounter = 10;
    82.             pageMin = 11;
    83.     }
    84.  
    85.     // Update is called once per frame
    86.     void Update ()
    87.     {
    88.     }
    89.  
    90.     public void StartNewModel()
    91.     {
    92.         panels = GameObject.Find("Canvas").GetComponentsInChildren<CanvasGroup>();
    93.         foreach (CanvasGroup panel in panels)
    94.         {
    95.             panel.alpha = 0;
    96.             panel.interactable = false;
    97.             panel.blocksRaycasts = false;
    98.         }
    99.         nmp = GameObject.FindGameObjectWithTag("NewModelPanel");
    100.         nmp.GetComponent<CanvasGroup>().alpha = 1;
    101.         nmp.GetComponent<CanvasGroup>().interactable = true;
    102.         nmp.GetComponent<CanvasGroup>().blocksRaycasts = true;
    103.     }
    104.  
    105.     public void ApplyNumber()
    106.     {
    107.         probinputfield.SetActive(true);
    108.         mainCanvas = GameObject.Find("Canvas");
    109.         mainCanvas.transform.Find("NMP10").gameObject.SetActive(true);
    110.         int x = 0;
    111.         switch(neighborEffectsDropdown.value)
    112.         {
    113.             case 0:
    114.                 x = 1;
    115.                 break;
    116.             case 1:
    117.                 x = 5;
    118.                 break;
    119.             case 2:
    120.                 x = 9;
    121.                 break;
    122.             case 3:
    123.                 x = 13;
    124.                 break;
    125.         }
    126.    
    127.         {
    128.             {
    129.                 GameObject[] inputFields = GameObject.FindGameObjectsWithTag("InstantiatedInputFields");
    130.                 GameObject[] cellPages = GameObject.FindGameObjectsWithTag("InstantiatedCellPages");
    131.                 foreach (GameObject inputField in inputFields)
    132.                 {
    133.                     GameObject.Destroy(inputField);
    134.                 }
    135.                 foreach (GameObject cellPage in cellPages)
    136.                 {
    137.                     GameObject.Destroy(cellPage);
    138.                 }
    139.             }
    140.             applyActive = true;
    141.             GameObject nmpcellclone;
    142.             GameObject probinputfieldclone;
    143.             nmpcell = GameObject.Find("NMP10");
    144.             numbercelltype = GameObject.FindGameObjectWithTag("numbercelltype").GetComponent<Text>();
    145.             pageMax = ((int.Parse(numbercelltype.text)) + pageCounter);
    146.             for (int pageNumber = 0; pageNumber < (int.Parse(numbercelltype.text) - 1); pageNumber++)
    147.             {
    148.                 for (int i = 0; i < x; ++i)
    149.                 {
    150.                     probinputfieldclone = Instantiate(probinputfield);
    151.                     probinputfieldclone.transform.SetParent(GameObject.Find("1stProbContent").transform, true);
    152.                     probinputfieldclone.transform.position = new Vector3(probinputfield.transform.position.x, probinputfield.transform.position.y - 20 - ((pageNumber * 200) + (i * 40)), probinputfield.transform.position.z);
    153.                     probinputfieldclone.tag = ("InstantiatedInputFields");
    154.                     probinputfieldclone.name = ("ProbInputField" + "1." + (pageNumber + 1) + "." + i);
    155.                     probinputfieldclone.transform.Find("ProbTransText").GetComponent<Text>().resizeTextMaxSize = 28;
    156.                     probinputfieldclone.transform.Find("ProbTransText").GetComponent<Text>().resizeTextForBestFit = true;
    157.                 }
    158.             }
    159.             probinputfield.SetActive(false);
    160.             for (int pageNumber = 1; pageNumber < (int.Parse(numbercelltype.text) + 1); pageNumber++)
    161.             {
    162.                 nmpcellclone = Instantiate(nmpcell);
    163.                 nmpcellclone.transform.SetParent(GameObject.Find("Canvas").transform, false);
    164.                 nmpcellclone.tag = ("InstantiatedCellPages");
    165.                 nmpcellclone.name = ("NMP" + "1" + pageNumber);
    166.                 probtitles = GameObject.FindGameObjectsWithTag("ProbTitle");
    167.                 foreach (GameObject probtitle in probtitles)
    168.                 {
    169.                     surroundingCells = int.Parse(probtitle.transform.parent.name.Remove(0, 18));
    170.                     string toProbCellPre = probtitle.transform.parent.name.Remove(0, 16);
    171.                     toProbCell = int.Parse(toProbCellPre.Remove(1, (toProbCellPre.Length - 1)));
    172.                     int fromProbCell = int.Parse(probtitle.transform.parent.parent.parent.parent.name.Remove(0, 4));
    173.                     NMP1probTitle = probtitle.GetComponent<Text>();
    174.                     if (fromProbCell == toProbCell)
    175.                     {
    176.                         NMP1probTitle.text = "Transformation Probability (" + fromProbCell + " to " + (toProbCell + 1) + "), with " + surroundingCells + " surrounding type " + (toProbCell + 1) + " cell(s)";
    177.                     }
    178.                     if (fromProbCell < toProbCell)
    179.                     {
    180.                         NMP1probTitle.text = "Transformation Probability (" + fromProbCell + " to " + (toProbCell + 1) + "), with " + surroundingCells + " surrounding type " + (toProbCell + 1) + " cell(s)";
    181.                     }
    182.                     if (fromProbCell > toProbCell)
    183.                     {
    184.                         NMP1probTitle.text = "Transformation Probability (" + fromProbCell + " to " + toProbCell + "), with " + surroundingCells + " surrounding type " + toProbCell + " cell(s)";
    185.                     }
    186.                 }
    187.             }
    188.             celltitles = GameObject.FindGameObjectsWithTag("CellTitle");
    189.             foreach (GameObject celltitle in celltitles)
    190.             {
    191.                 NMP1pageTitle = celltitle.GetComponent<Text>();
    192.                 NMP1pageTitle.text = "Cell Type " + celltitle.transform.parent.name.Remove(0, 4);
    193.             }
    194.         }
    195.     }
    196.  
    197.     public void Back1()
    198.     {
    199.         nmpnow = GameObject.Find("NMP" + (pageCounter - 1));
    200.         panels = GameObject.Find("Canvas").GetComponentsInChildren<CanvasGroup>();
    201.         foreach (CanvasGroup panel in panels)
    202.         {
    203.             panel.alpha = 0;
    204.             panel.interactable = false;
    205.             panel.blocksRaycasts = false;
    206.         }
    207.         if (pageCounter > pageMin)
    208.         {
    209.             nmpnow.GetComponent<CanvasGroup>().alpha = 1;
    210.             nmpnow.GetComponent<CanvasGroup>().interactable = true;
    211.             nmpnow.GetComponent<CanvasGroup>().blocksRaycasts = true;
    212.             pageCounter -= 1;
    213.         }
    214.         else if(pageCounter == pageMin)
    215.         {
    216.             probinputfield.SetActive(true);
    217.             nmp = GameObject.FindGameObjectWithTag("NewModelPanel");
    218.             nmp.GetComponent<CanvasGroup>().alpha = 1;
    219.             nmp.GetComponent<CanvasGroup>().interactable = true;
    220.             nmp.GetComponent<CanvasGroup>().blocksRaycasts = true;
    221.             pageCounter -= 1;
    222.         }
    223.         else if(pageCounter < pageMin)
    224.         {
    225.             mmp = GameObject.FindGameObjectWithTag("MainMenuPanel");
    226.             mmp.GetComponent<CanvasGroup>().alpha = 1;
    227.             mmp.GetComponent<CanvasGroup>().interactable = true;
    228.             mmp.GetComponent<CanvasGroup>().blocksRaycasts = true;
    229.         }
    230.     }
    231.     public void Next1()
    232.     {
    233.         countDiff = (pageMax - pageCounter);
    234.         confirm = GameObject.FindGameObjectWithTag("ConfirmBox");
    235.         designate = GameObject.FindGameObjectWithTag("DesignateCellTypes");
    236.         panels = GameObject.Find("Canvas").GetComponentsInChildren<CanvasGroup>();
    237.         foreach (CanvasGroup panel in panels)
    238.         {
    239.             panel.alpha = 0;
    240.             panel.interactable = false;
    241.             panel.blocksRaycasts = false;
    242.         }
    243.         if (pageCounter == pageMax)
    244.         {
    245.             confirm.GetComponent<CanvasGroup>().alpha = 1;
    246.             confirm.GetComponent<CanvasGroup>().interactable = true;
    247.             confirm.GetComponent<CanvasGroup>().blocksRaycasts = true;
    248.         }
    249.         else if(pageMax < pageMin)
    250.         {
    251.             designate.GetComponent<CanvasGroup>().alpha = 1;
    252.             designate.GetComponent<CanvasGroup>().interactable = true;
    253.             designate.GetComponent<CanvasGroup>().blocksRaycasts = true;
    254.         }
    255.         else if (pageCounter < pageMax)
    256.         {
    257.             nmpnow = GameObject.Find("NMP" + (pageCounter + 1));
    258.             nmpnow.GetComponent<CanvasGroup>().alpha = 1;
    259.             nmpnow.GetComponent<CanvasGroup>().interactable = true;
    260.             nmpnow.GetComponent<CanvasGroup>().blocksRaycasts = true;
    261.             pageCounter += 1;
    262.         }
    263.     }
    264.  
    265.     public void ConfirmYes()
    266.     {
    267.         nmpnow = GameObject.Find("NMP" + pageCounter);
    268.         foreach (CanvasGroup panel in panels)
    269.         {
    270.             panel.alpha = 0;
    271.             panel.interactable = false;
    272.             panel.blocksRaycasts = false;
    273.         }
    274.  
    275.         gridpanel = GameObject.Find("GridPanel");
    276.         {
    277.             gridpanel.GetComponent<CanvasGroup>().alpha = 1;
    278.             gridpanel.GetComponent<CanvasGroup>().interactable = true;
    279.             gridpanel.GetComponent<CanvasGroup>().blocksRaycasts = true;
    280.         }
    281.         GameObject.Find("FirstOrderGO").GetComponent<FirstOrderControllerScript>().CreateCA();
    282.     }
    283.  
    284.     public void StartCAButton()
    285.     {
    286.         GameObject.Find("FirstOrderGO").GetComponent<FirstOrderControllerScript>().StartCA();
    287.     }
    288.  
    289.     public void PauseButton()
    290.     {
    291.         continueCA = !continueCA;
    292.         pauseButtonText.text = continueCA ? "UnPaused" : "Paused";
    293.     }
    294.  
    295.     public void GridtoMain()
    296.     {
    297.         mainCanvas = GameObject.Find("Canvas");
    298.         cellpages = GameObject.FindGameObjectsWithTag("InstantiatedCellPages");
    299.         inputfields = GameObject.FindGameObjectsWithTag("InstantiatedInputFields");
    300.         probinputfield.SetActive(true);
    301.         pageCounter = 10;
    302.         pageMin = 11;
    303.         pageMax = 0;
    304.         foreach (GameObject cellpage in cellpages)
    305.         {
    306.             Destroy(cellpage);
    307.         }
    308.         foreach (GameObject inputfield in inputfields)
    309.         {
    310.             Destroy(inputfield);
    311.         }
    312.         gridpanel = GameObject.Find("GridPanel");
    313.         {
    314.             gridpanel.GetComponent<CanvasGroup>().alpha = 0;
    315.             gridpanel.GetComponent<CanvasGroup>().interactable = false;
    316.             gridpanel.GetComponent<CanvasGroup>().blocksRaycasts = false;
    317.         }
    318.         {
    319.             mmp = GameObject.FindGameObjectWithTag("MainMenuPanel");
    320.             mmp.GetComponent<CanvasGroup>().alpha = 1;
    321.             mmp.GetComponent<CanvasGroup>().interactable = true;
    322.             mmp.GetComponent<CanvasGroup>().blocksRaycasts = true;
    323.         }
    324.         mainCanvas.transform.Find("NMP10").gameObject.SetActive(true);
    325.         GameObject.Find("FirstOrderGO").GetComponent<FirstOrderControllerScript>().ClearGrid();
    326.     }
    327.  
    328.     public void SaveImage()
    329.     {
    330.         GameObject.Find("FirstOrderGO").GetComponent<FirstOrderControllerScript>().SaveImage();
    331.     }
    332.  
    333.     public void SaveImages()
    334.     {
    335.         GameObject.Find("FirstOrderGO").GetComponent<FirstOrderControllerScript>().SaveImages();
    336.     }
    337.  
    338.     public void SaveCellCount()
    339.     {
    340.         GameObject.Find("FirstOrderGO").GetComponent<FirstOrderControllerScript>().SaveIterationCount();
    341.     }
    342.  
    343.     public void ResetGrid()
    344.     {
    345.         GameObject.Find("FirstOrderGO").GetComponent<FirstOrderControllerScript>().ResetGrid();
    346.     }
    347.  
    348.     public void ConfirmNo()
    349.     {
    350.         nmpnow = GameObject.Find("NMP" + pageCounter);
    351.         foreach (CanvasGroup panel in panels)
    352.         {
    353.             panel.alpha = 0;
    354.             panel.interactable = false;
    355.             panel.blocksRaycasts = false;
    356.         }
    357.         {
    358.             nmpnow.GetComponent<CanvasGroup>().alpha = 1;
    359.             nmpnow.GetComponent<CanvasGroup>().interactable = true;
    360.             nmpnow.GetComponent<CanvasGroup>().blocksRaycasts = true;
    361.         }
    362.     }
    363.  
    364.     public void Okay()
    365.     {
    366.         foreach (CanvasGroup panel in panels)
    367.         {
    368.             panel.alpha = 0;
    369.             panel.interactable = false;
    370.             panel.blocksRaycasts = false;
    371.         }
    372.         nmp = GameObject.FindGameObjectWithTag("NewModelPanel");
    373.         nmp.GetComponent<CanvasGroup>().alpha = 1;
    374.         nmp.GetComponent<CanvasGroup>().interactable = true;
    375.         nmp.GetComponent<CanvasGroup>().blocksRaycasts = true;
    376.     }
    377.     public void Quit()
    378.     {
    379.         Application.Quit();
    380.     }
    381. }
    Code (csharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using System.IO;
    6. using System.Linq;
    7.  
    8. public class FirstOrderControllerScript : MonoBehaviour
    9. {
    10.     int amountOfCellTypes;
    11.     int gridWidth;
    12.     int gridHeight;
    13.     Buttons buttonscript;
    14.     List<float> ratios = new List<float>();
    15.     List<int> colors = new List<int>();
    16.     List<List<int>> fullCount = new List<List<int>>();
    17.     Dictionary<int, Color> dict;
    18.     Dropdown neighborEffectsDropdown;
    19.     List<byte[]> imageList = new List<byte[]>();
    20.  
    21.     CA myCA;
    22.     Texture2D tex;
    23.     Sprite board;
    24.     SpriteRenderer sr;
    25.     private int iterations = 0;
    26.     public Text iterText;
    27.  
    28.     private void Awake()
    29.     {
    30.     }
    31.  
    32.     private void Start()
    33.     {
    34.         buttonscript = GameObject.Find("ButtonScript").GetComponent<Buttons>();
    35.         neighborEffectsDropdown = GameObject.FindGameObjectWithTag("NeighborhoodDropdown").GetComponent<Dropdown>();
    36.  
    37.         //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.
    38.         //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.
    39.         //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.
    40.         dict = new Dictionary<int, Color>();
    41.         dict.Add(0, Color.black);
    42.         dict.Add(1, Color.blue);
    43.         dict.Add(2, Color.cyan);
    44.         dict.Add(3, Color.gray);
    45.         dict.Add(4, Color.green);
    46.         dict.Add(5, Color.magenta);
    47.         dict.Add(6, Color.red);
    48.         dict.Add(7, Color.white);
    49.         dict.Add(8, Color.yellow);
    50.     }
    51.  
    52.     private void Update()
    53.     {
    54.         if (Input.GetKeyUp(KeyCode.E))
    55.         {
    56.             OneIteration();
    57.             UpdateBoard();
    58.         }
    59.         if (Input.GetKey(KeyCode.C))
    60.         {
    61.             OneIteration();
    62.             UpdateBoard();
    63.         }
    64.  
    65.         if (buttonscript.continueCA == true)
    66.         {
    67.             OneIteration();
    68.             UpdateBoard();
    69.         }
    70.     }
    71.  
    72.     public void OneIteration()
    73.     {
    74.         myCA.OneIteration();
    75.         iterations++;
    76.         iterText.text = "Iteration: " + iterations.ToString();
    77.         List<int> currentCellCount = new List<int>();
    78.         currentCellCount.AddRange(CA.stateCount);
    79.         fullCount.Add(currentCellCount);
    80.         StoreImage();
    81.     }
    82.  
    83.     public void CreateCA()
    84.     {
    85.         NType x = NType.None;
    86.         int y = 0;
    87.         switch (neighborEffectsDropdown.value)
    88.         {
    89.             case 0:
    90.                 x = NType.None;
    91.                 y = 0;
    92.                 break;
    93.             case 1:
    94.                 x = NType.VonNeumann;
    95.                 y = 4;
    96.                 break;
    97.             case 2:
    98.                 x = NType.Moore;
    99.                 y = 8;
    100.                 break;
    101.             case 3:
    102.                 x = NType.ExtendedVonNeumann;
    103.                 y = 12;
    104.                 break;
    105.         }
    106.         amountOfCellTypes = int.Parse(GameObject.FindGameObjectWithTag("numbercelltype").GetComponent<Text>().text);
    107.         gridWidth = int.Parse(GameObject.Find("Horigrid").GetComponent<Text>().text);
    108.         gridHeight = int.Parse(GameObject.Find("Vertgrid").GetComponent<Text>().text);
    109.  
    110.         myCA = new CA(gridWidth, gridHeight, amountOfCellTypes, x);
    111.  
    112.         for (int i = 0; i < amountOfCellTypes; ++i)
    113.         {
    114.             string numstring = (i + 1).ToString();
    115.             GameObject basePage = GameObject.Find("NMP1" + numstring);
    116.             ratios.Add(float.Parse(basePage.transform.Find("StartNumberInputField").transform.Find("Text").GetComponent<Text>().text));
    117.             colors.Add(basePage.transform.Find("ColorDropdown").GetComponent<Dropdown>().value);
    118.             for (int j = 1; j < amountOfCellTypes; ++j)
    119.             {
    120.                 GameObject probGrandparent = basePage.transform.Find("ProbabilityScrollView").transform.Find("1stProbContent").gameObject;
    121.                 for (int n = 0; n <= y; ++n)
    122.                 {
    123.                     GameObject probParent = probGrandparent.transform.Find("ProbInputField1." + j + "." + n).gameObject;
    124.                     string startString = probParent.transform.Find("ProbTransText").GetComponent<Text>().text.Remove(0, 28);
    125.                     string endString = probParent.transform.Find("ProbTransText").GetComponent<Text>().text.Remove(0, 33);
    126.                     float val = float.Parse(probParent.transform.Find("ProbTransValue").GetComponent<Text>().text);
    127.                     int startState = (int.Parse(startString.Remove(1, (startString.Length - 1)))) - 1;
    128.                     int endState = (int.Parse(endString.Remove(1, (endString.Length - 1)))) - 1;
    129.                     myCA.SetStateInfo(startState, endState, n, val);
    130.                 }
    131.             }
    132.         }
    133.  
    134.         myCA.InitializeGrid(ratios);
    135.     }
    136.  
    137.     public void StartCA()
    138.     {
    139.         tex = new Texture2D((gridWidth), (gridHeight));
    140.         tex.filterMode = FilterMode.Point;
    141.         board = Sprite.Create(tex, new Rect(Vector2.zero, new Vector2((gridWidth), (gridHeight))), Vector2.zero, 1f);
    142.         sr = gameObject.AddComponent<SpriteRenderer>();
    143.         sr.transform.localScale = new Vector2((450f / gridWidth), (450f / gridHeight));
    144.         sr.transform.localPosition = new Vector2((sr.transform.position.x - (((gridWidth) / 2) * sr.transform.localScale.x)), (sr.transform.position.y - (((gridHeight) / 2) * sr.transform.localScale.y)));
    145.         sr.GetComponent<SpriteRenderer>().sortingLayerName = "Foreground";
    146.         sr.sprite = board;
    147.         UpdateBoard();
    148.     }
    149.  
    150.     private void UpdateBoard()
    151.     {
    152.         Color tileColor;
    153.         for (int i = 0; i < gridWidth; ++i)
    154.         {
    155.             for (int j = 0; j < gridHeight; ++j)
    156.             {
    157.                 tileColor = dict[colors[myCA.GetCellState(i, j)]];
    158.                 tex.SetPixel(i, j, tileColor);
    159.             }
    160.         }
    161.         tex.Apply();
    162.     }
    163.  
    164.     public void SaveImage()
    165.     {
    166.         byte[] bytes = tex.EncodeToPNG();
    167.         Directory.CreateDirectory(Application.persistentDataPath + "/CA Image Captures");
    168.         File.WriteAllBytes(Application.persistentDataPath + "/CA Image Captures/" + System.DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + " Iteration " + iterations + ".png", bytes);
    169.     }
    170.  
    171.     public void StoreImage()
    172.     {
    173.         byte[] bytes = tex.EncodeToPNG();
    174.         var newBytes = bytes;
    175.         imageList.Add(newBytes);
    176.     }
    177.  
    178.     public void SaveImages()
    179.     {
    180.         string newFolder = Application.persistentDataPath + "/" + System.DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + "/";
    181.         Directory.CreateDirectory(newFolder);
    182.         for(int i = 0; i < imageList.Count; ++i)
    183.         {
    184.             File.WriteAllBytes(newFolder + "Iteration " + i + ".png", imageList[i]);
    185.         }
    186.     }
    187.  
    188.     public void SaveIterationCount()
    189.     {
    190.         using (StreamWriter wt = File.AppendText(Application.persistentDataPath + System.DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + " CA Run.txt"))
    191.         {
    192.  
    193.             for (int i = 0; i < fullCount.Count; ++i)
    194.             {
    195.                 wt.Write("Iteration: " + i);
    196.                 for (int j = 0; j < amountOfCellTypes; ++j)
    197.                 {
    198.                     string cellTypeString = (j + 1).ToString();
    199.                     wt.Write(" Cell Type " + cellTypeString + ": " + fullCount[i][j]);
    200.                 }
    201.                 wt.WriteLine();
    202.             }
    203.             wt.Close();
    204.         }
    205.     }
    206.  
    207.     public void ClearGrid()
    208.     {
    209.         Destroy(tex);
    210.         Destroy(board);
    211.         Destroy(sr);
    212.         iterations = 0;
    213.         ratios.Clear();
    214.         colors.Clear();
    215.         fullCount.Clear();
    216.         imageList.Clear();
    217.     }
    218.  
    219.     public void ResetGrid()
    220.     {
    221.         ClearGrid();
    222.         CreateCA();
    223.     }
    224. }
    225.  
    Code (csharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class Neighborhood
    7. {
    8.     static Point[] MooreOffsets = new Point[8]
    9.     {
    10.         new Point(0,1),
    11.         new Point(1,1),
    12.         new Point(1,0),
    13.         new Point(1,-1),
    14.         new Point(0,-1),
    15.         new Point(-1,-1),
    16.         new Point(-1,0),
    17.         new Point(-1,1)
    18.     };
    19.  
    20.     static Point[] vonNeumannOffsets = new Point[4]
    21.     {
    22.         new Point(0,1),
    23.         new Point(1,0),
    24.         new Point(0,-1),
    25.         new Point(-1,0),
    26.     };
    27.  
    28.     static Point[] noOffsets = new Point[0]
    29.     {
    30.     };
    31.  
    32.     static Point[] extendedVonNeumannOffsets = new Point[12]
    33.     {
    34.         new Point(0,1),
    35.         new Point(1,1),
    36.         new Point(1,0),
    37.         new Point(1,-1),
    38.         new Point(0,-1),
    39.         new Point(-1,-1),
    40.         new Point(-1,0),
    41.         new Point(-1,1),
    42.         new Point(0,2),
    43.         new Point(2,0),
    44.         new Point(0,-2),
    45.         new Point(-2,0),
    46.     };
    47.  
    48.     private NType neighborType;
    49.  
    50.     public NType NeighborType
    51.     {
    52.         get { return neighborType; }
    53.         set { neighborType = value; }
    54.     }
    55.  
    56.     public Neighborhood(NType type)
    57.     {
    58.         neighborType = type;
    59.     }
    60.  
    61.     public List<Point> GetNeighbors(int x, int y)
    62.     {
    63.         return GetNeighbors(new Point(x, y));
    64.     }
    65.  
    66.     public List<Point> GetNeighbors(Point cell)
    67.     {
    68.         switch (neighborType)
    69.         {
    70.             case NType.Moore:
    71.                 return GetMooreNeighbors(cell);
    72.             case NType.VonNeumann:
    73.                 return GetVonNeumannNeighbors(cell);
    74.             case NType.ExtendedVonNeumann:
    75.                 return GetExtendedVonNeumannNeighbors(cell);
    76.             case NType.None:
    77.                 return GetNoNeighbors(cell);
    78.             default:
    79.                 throw new ArgumentException("Unknown Neighborhood type in GetNeighbors");
    80.         }
    81.     }
    82.  
    83.     private List<Point> GetMooreNeighbors(Point cell)
    84.     {
    85.         List<Point> neighbors = new List<Point>();
    86.         foreach (Point p in MooreOffsets)
    87.             neighbors.Add(cell + p);
    88.         return neighbors;
    89.     }
    90.  
    91.     private List<Point> GetVonNeumannNeighbors (Point cell)
    92.     {
    93.         List<Point> neighbors = new List<Point>();
    94.         foreach (Point p in vonNeumannOffsets)
    95.             neighbors.Add(cell + p);
    96.         return neighbors;
    97.     }
    98.  
    99.     private List<Point> GetExtendedVonNeumannNeighbors(Point cell)
    100.     {
    101.         List<Point> neighbors = new List<Point>();
    102.         foreach (Point p in extendedVonNeumannOffsets)
    103.             neighbors.Add(cell + p);
    104.         return neighbors;
    105.     }
    106.  
    107.     private List<Point> GetNoNeighbors(Point cell)
    108.     {
    109.         List<Point> neighbors = new List<Point>();
    110.         foreach (Point p in noOffsets)
    111.             neighbors.Add(cell + p);
    112.         return neighbors;
    113.     }
    114.  
    115.     public int GetNeighborSize()
    116.     {
    117.         switch (neighborType)
    118.         {
    119.             case NType.Moore:
    120.                 return 8;
    121.             case NType.VonNeumann:
    122.                 return 4;
    123.             case NType.ExtendedVonNeumann:
    124.                 return 12;
    125.             case NType.None:
    126.                 return 0;
    127.             default:
    128.                 throw new ArgumentException("Unknown Neighborhood type in GetNeighborSize");
    129.         }
    130.     }
    131.  
    132. }
    133.  
    134. public enum NType
    135. {
    136.     ExtendedVonNeumann,
    137.     VonNeumann,
    138.     Moore,
    139.     None
    140. }

    I was going to attach the project as well, but for some reason 7zip didn't compress it well enough. I'll look at that later.
     
    Last edited: Mar 6, 2017
  4. EternalAmbiguity

    EternalAmbiguity

    Joined:
    Dec 27, 2014
    Posts:
    2,142
    Okay, for some reason my "Libraries" folder in the project is enormous, over 25 MB. I really don't know why. Anyway, because of that I can't properly compress the whole project to less than 4MB. So I compressed the rest of the project, and then the libraries folder separately. So if you extract both, and put the libraries folder into the project folder, it should run correctly.

    Anyway, it runs way faster than it used to for some reason, possibly because of the textures being ten times smaller. Additionally, I cleaned up the UI a little bit (got rid of the runs and iterations stuff when creating a grid). Also made a toggle for if the user wants to save an image of each iteration. Now, it checks that toggle each time OneIteration runs. This is probably a bad idea--it should only check it once at the beginning, and then select the proper variant of OneIteration based on the toggle. But I'm not exactly sure how I would do that.

    Additionally, I've looked a bit over the multi-threading stuff but it might be better to wait until Unity gets its act together and updates the program's .NET capabilities. I imagine the multithreading could be done a couple ways: by having the system automatically create threads as it wishes, or by creating them myself and keeping them alive during the running of the CA. Not sure which would be better.

    As for this stuff, I imagine the next big step (and I thought I was done...) might be to have the user be able to change certain parts of the grid before running it. I'll take a look at that. Either that or the serialization and save/load file.
     

    Attached Files: