void GenerateGrid()
    {
        int      spawnedStores = 0;
        GridData data          = GridData.GetInstance();

        while (spawnedStores < storeCount)
        {
            int x = UnityEngine.Random.Range(0, BoardWidth);
            int y = UnityEngine.Random.Range(0, BoardWidth);

            EntityInfo cellValue;
            data.gridStatus.TryGetValue(GridData.ConvertToHash(x, y), out cellValue);
            if (cellValue.type != (int)Tiles.Store)
            {
                cellValue = new EntityInfo {
                    type = (int)Tiles.Store
                };
                data.gridStatus.TryAdd(GridData.ConvertToHash(x, y), cellValue);
                Instantiate(StorePrefab, new Vector3(x, 2, y), Quaternion.identity);
                spawnedStores++;
            }
        }

        // FIX: This could maybe be parallelized?
        for (int i = 0; i < rockSpawnAttempts; i++)
        {
            TrySpawnRock();
        }
    }
    // rock spawner from the original sim
    // except currently spawns cubes rather
    // than rectangles
    void TrySpawnRock()
    {
        GridData data   = GridData.GetInstance();
        int      width  = UnityEngine.Random.Range(0, 4);
        int      height = UnityEngine.Random.Range(0, 4);
        int      rockX  = UnityEngine.Random.Range(0, BoardWidth - width);
        int      rockY  = UnityEngine.Random.Range(0, BoardWidth - height);
        RectInt  rect   = new RectInt(rockX, rockY, width, height);

        bool blocked = false;

        for (int x = rockX; x <= rockX + width; x++)
        {
            for (int y = rockY; y <= rockY + height; y++)
            {
                EntityInfo tileValue;
                if (data.gridStatus.TryGetValue(GridData.ConvertToHash(x, y), out tileValue))
                {
                    blocked = true;
                    break;
                }
            }
            if (blocked)
            {
                break;
            }
        }
        if (blocked == false)
        {
            //TODO: Combine rocks into groups

            for (int x = rockX; x <= rockX + width; x++)
            {
                for (int y = rockY; y <= rockY + height; y++)
                {
                    // get some new rock entities and add them to an array
                    // so we can find them later
                    Entity     tmp  = entityManager.Instantiate(rockEntity);
                    EntityInfo info = new EntityInfo {
                        type = (int)Tiles.Rock, specificEntity = tmp
                    };
                    data.gridStatus.TryAdd(GridData.ConvertToHash(x, y), info);
                    //Debug.Log("entity info is: " + x + " " + y + " index: " + entityIndex + " " + tmp.Index);
                    entityManager.SetComponentData(tmp, new Translation()
                    {
                        Value = new Unity.Mathematics.float3(x, height * 0.25f, y)
                    });
                }
            }
        }
    }
        public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
        {
            var translations  = chunk.GetNativeArray(TranslationTypeHandle);
            var entityIntents = chunk.GetNativeArray(EntityInfoHandle);
            var entities      = chunk.GetNativeArray(EntityType);

            for (var i = 0; i < chunk.Count; i++)
            {
                Tiles state = (Tiles)entityIntents[i].type;

                switch (state)
                {
                case Tiles.Rock:
                    //Debug.Log("destroying rock");
                    // destroy rock
                    addRemoveTags.Enqueue(new TagInfo
                    {
                        shouldRemove = 0, entity = entityIntents[i].specificEntity, type = Tags.Disable
                    });
                    // remove perform task and add needs task tags
                    addRemoveTags.Enqueue(new TagInfo {
                        shouldRemove = 0, entity = entities[i], type = Tags.NeedsTask
                    });
                    addRemoveTags.Enqueue(new TagInfo {
                        shouldRemove = 1, entity = entities[i], type = Tags.PerformTask
                    });
                    break;

                case Tiles.Till:
                    float      tillBlockHeight = 0.25f;
                    EntityInfo tillInfo        = new EntityInfo {
                        type = (int)Tiles.Till
                    };
                    if (
                        grid.TryAdd(GridData.ConvertToHash((int)translations[i].Value.x, (int)translations[i].Value.z),
                                    tillInfo))
                    {
                        float3 pos = new float3((int)translations[i].Value.x, tillBlockHeight,
                                                (int)translations[i].Value.z);

                        changes.Enqueue(new float2((int)pos.x, (int)pos.z));
                    }

                    // add needs task and remove perform task tags
                    addRemoveTags.Enqueue(new TagInfo {
                        shouldRemove = 0, entity = entities[i], type = Tags.NeedsTask
                    });
                    addRemoveTags.Enqueue(new TagInfo {
                        shouldRemove = 1, entity = entities[i], type = Tags.PerformTask
                    });
                    break;

                case Tiles.Plant:
                    // since the plant needs to be instantiated and then cached
                    // into the hash table it's done in the main thread
                    // add needs task and remove perform task tags
                    addRemoveTags.Enqueue(new TagInfo {
                        shouldRemove = 0, entity = entities[i], type = Tags.NeedsTask
                    });
                    addRemoveTags.Enqueue(new TagInfo {
                        shouldRemove = 1, entity = entities[i], type = Tags.PerformTask
                    });

                    break;

                case Tiles.Harvest:
                    EntityInfo harvestInfo = new EntityInfo {
                        type = (int)Tiles.Till
                    };
                    if (plantInfo.HasComponent(entityIntents[i].specificEntity))
                    {
                        PlantComponent plant = plantInfo[entityIntents[i].specificEntity];
                        if (plant.reserveIndex == entities[i].Index &&
                            grid.TryAdd(
                                GridData.ConvertToHash((int)translations[i].Value.x, (int)translations[i].Value.z),
                                harvestInfo))
                        {
                            //UnityEngine.Debug.Log("harvesting : " + entityIntents[i].specificEntity.Index);

                            // plant needs to follow the farmer
                            PlantComponent plantData2 = new PlantComponent
                            {
                                timeGrown      = plantGrowthMax,
                                state          = (int)PlantState.Following,
                                farmerToFollow = entities[i],
                                reserveIndex   = plant.reserveIndex
                            };

                            setInfo.Enqueue(new ComponentSetInfo
                            {
                                entity         = entityIntents[i].specificEntity,
                                plantComponent = plantData2
                            });
                        }
                        else if (plant.reserveIndex != entities[i].Index)
                        {
                            entityIntents[i] = new EntityInfo
                            {
                                specificEntity = entityIntents[i].specificEntity,
                                type           = (short)Tiles.None
                            };
                        }
                    }
                    else
                    {
                        entityIntents[i] = new EntityInfo
                        {
                            specificEntity = entityIntents[i].specificEntity,
                            type           = (short)Tiles.None
                        };
                    }

                    // remove perform task and add needs task
                    addRemoveTags.Enqueue(new TagInfo {
                        shouldRemove = 0, entity = entities[i], type = Tags.NeedsTask
                    });
                    addRemoveTags.Enqueue(new TagInfo {
                        shouldRemove = 1, entity = entities[i], type = Tags.PerformTask
                    });

                    break;

                case Tiles.Store:
                    // multiple entities can try to delete this plant at the store
                    // the single threaded job at the end takes care of this

                    // we need to remove the plant from the farmer
                    PlantComponent plantData = new PlantComponent
                    {
                        timeGrown = plantGrowthMax,
                        state     = (int)PlantState.Deleted
                    };

                    setInfo.Enqueue(new ComponentSetInfo
                    {
                        entity         = entityIntents[i].specificEntity,
                        plantComponent = plantData
                    });

                    // remove perform task and add needs task tags
                    addRemoveTags.Enqueue(new TagInfo {
                        shouldRemove = 0, entity = entities[i], type = Tags.NeedsTask
                    });
                    addRemoveTags.Enqueue(new TagInfo {
                        shouldRemove = 1, entity = entities[i], type = Tags.PerformTask
                    });

                    // and actually sell stuff here
                    unsafe
                    {
                        Interlocked.Increment(ref ((int *)plantsSold.GetUnsafePtr())[0]);
                    }

                    break;

                default:
                    break;
                }
            }
        }
        // randomly determines a task and then finds the right tiles that
        // will help the task occur
        public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
        {
            var translations  = chunk.GetNativeArray(TranslationTypeHandle);
            var movements     = chunk.GetNativeArray(MovementTypeHandle);
            var entityIntents = chunk.GetNativeArray(EntityInfoHandle);
            var entities      = chunk.GetNativeArray(EntityType);

            for (var i = 0; i < chunk.Count; i++)
            {
                //
                // determine what the task for this entity is
                //
                Tiles taskValue = (Tiles)(randArray[(nextIndex + entities[i].Index) % randArray.Length] % 4) + 1;
                nextIndex++;

                if (entityIntents[i].type == (int)Tiles.Harvest)
                {
                    // we just harvested and now need to get the plant
                    // to the store
                    taskValue = Tiles.Store;
                }

                //
                // look for the best tile for performing that task
                //
                float2 pos = new float2(translations[i].Value.x, translations[i].Value.z);
                float2 foundLocation;
                switch (taskValue)
                {
                case Tiles.Rock:
                    foundLocation = GridData.Search(randArray, nextIndex, gridHashMap, pos, radiusForSearch,
                                                    (int)taskValue, gridSize, gridSize);
                    nextIndex++;
                    break;

                case Tiles.Till:
                    // default is currently Till
                    foundLocation = GridData.Search(randArray, nextIndex, gridHashMap, pos, radiusForSearch, 0,
                                                    gridSize, gridSize);
                    nextIndex++;
                    if (foundLocation.x == -1)
                    {
                        foundLocation = GridData.Search(randArray, nextIndex, gridHashMap, pos, radiusForSearch * 3,
                                                        0, gridSize, gridSize);
                        nextIndex++;
                    }

                    break;

                case Tiles.Plant:
                    // need to search for tilled soil
                    foundLocation = GridData.Search(randArray, nextIndex, gridHashMap, pos, radiusForSearch,
                                                    (int)Tiles.Till, gridSize, gridSize);
                    nextIndex++;
                    if (foundLocation.x == -1)
                    {
                        foundLocation = GridData.Search(randArray, nextIndex, gridHashMap, pos, radiusForSearch * 3,
                                                        (int)Tiles.Till, gridSize, gridSize);
                        nextIndex++;
                    }

                    break;

                case Tiles.Harvest:
                    // searches for the plants to go harvest them
                    foundLocation =
                        GridData.FindMaturePlant(randArray, nextIndex, gridHashMap, pos, radiusForSearch,
                                                 (int)Tiles.Plant, gridSize, gridSize,
                                                 ref IsPlantType, plantGrowthMax);
                    nextIndex++;
                    if (foundLocation.x == -1)
                    {
                        foundLocation = GridData.FindMaturePlant(randArray, nextIndex, gridHashMap, pos,
                                                                 radiusForSearch * 3, (int)Tiles.Plant, gridSize, gridSize,
                                                                 ref IsPlantType, plantGrowthMax);
                        nextIndex++;
                    }

                    break;

                default:
                    // searches for the stores to go and sell a plant
                    foundLocation = GridData.Search(randArray, nextIndex, gridHashMap, pos, radiusForSearch,
                                                    (int)Tiles.Store, gridSize, gridSize);
                    nextIndex++;
                    // no store close by
                    if (foundLocation.x == -1)
                    {
                        // need to find somewhere to get rid of the plant
                        foundLocation = GridData.Search(randArray, nextIndex, gridHashMap, pos, gridSize,
                                                        (int)Tiles.Store, gridSize, gridSize);
                        nextIndex++;
                    }

                    break;
                }


                //UnityEngine.Debug.Log("finding new task : " + taskValue + " for entity " + entities[i].Index +
                //                      " found " + foundLocation.x);

                //
                // If a tile was found, set up the data for the task
                // if there's a rock in the way, then just go break the rock
                //
                if (foundLocation.x != -1 && foundLocation.y != -1)
                {
                    float2 findMiddle = MovementSystem.FindMiddlePos(pos, foundLocation);
                    var    rockPos    = GridData.FindTheRock(gridHashMap, pos, findMiddle, foundLocation, gridSize, gridSize);
                    //Debug.Log(index + " Start: " + pos.x + " " + pos.y + " middle : " + findMiddle.x + " " + findMiddle.y + " target pos : " +
                    //    foundLocation.x + " " + foundLocation.y + " " + rockPos + " intention: " + taskValue);
                    if ((int)rockPos.x != -1 && (int)rockPos.y != -1)
                    {
                        // we found a rock so go mine it on the path
                        // if rock position on an x or y direction then don't change the middle
                        // otherwise re-find the middle
                        if ((int)rockPos.x == (int)pos.x || (int)rockPos.y == (int)pos.y)
                        {
                            findMiddle = MovementSystem.FindMiddlePos(pos, rockPos);
                        }
                        //Debug.Log("Updated rock position to: " + rockPos + "Actor is now chasing a rock");

                        rockPos = new float2(rockPos.x + 0.5f, rockPos.y + 0.5f);
                        var data = new MovementComponent
                        {
                            startPos  = pos,
                            speed     = 2,
                            targetPos = rockPos,
                            middlePos = findMiddle,
                        };
                        movements[i] = data;

                        // if we are on the way to the store then destroy the plant and
                        // mine the rock
                        if (taskValue == Tiles.Store)
                        {
                            // destroy the plant as there's a rock in the way or no place to take it

                            //UnityEngine.Debug.Log("plant should be destroyed on farmer");
                            PlantComponent plantInfo = new PlantComponent
                            {
                                timeGrown = plantGrowthMax,
                                state     = (int)PlantState.Deleted,
                            };
                            setInfo.Enqueue(new ComponentSetInfo
                            {
                                entity = entityIntents[i].specificEntity, plantComponent = plantInfo
                            });
                        }

                        // get the index into the array of rocks so that we can find it
                        // to destroy it
                        EntityInfo fullRockData =
                            GridData.getFullHashValue(gridHashMap, (int)rockPos.x, (int)rockPos.y);
                        entityIntents[i] = fullRockData;
                        //Debug.Log("rock task happening : " + rockEntityIndex + " " + tmp.Index);

                        // remove needs task and add moving tag
                        addRemoveTags.Enqueue(new TagInfo {
                            shouldRemove = 1, entity = entities[i], type = Tags.NeedsTask
                        });
                        addRemoveTags.Enqueue(new TagInfo {
                            shouldRemove = 0, entity = entities[i], type = Tags.Moving
                        });

                        int key = GridData.ConvertToHash((int)rockPos.x, (int)rockPos.y);
                        removals.Enqueue(new RemovalInfo {
                            key = key, requestingEntity = entities[i]
                        });
                    }
                    else
                    {
                        foundLocation = new float2(foundLocation.x + 0.5f, foundLocation.y + 0.5f);
                        var data = new MovementComponent
                        {
                            startPos  = pos,
                            speed     = 2,
                            targetPos = foundLocation,
                            middlePos = findMiddle,
                        };
                        movements[i] = data;

                        //Debug.Log("doing a task and about to move: " + pos.x + " " + pos.y +
                        //    " target is : " + data.targetPos.x + " " + data.targetPos.y);
                        //Debug.Log("rock value: " + rockPos);

                        // remove needs task and add moving tag
                        addRemoveTags.Enqueue(new TagInfo {
                            shouldRemove = 1, entity = entities[i], type = Tags.NeedsTask
                        });
                        addRemoveTags.Enqueue(new TagInfo {
                            shouldRemove = 0, entity = entities[i], type = Tags.Moving
                        });

                        int key;
                        switch (taskValue)
                        {
                        case Tiles.Rock:
                            key = GridData.ConvertToHash((int)foundLocation.x, (int)foundLocation.y);
                            // get the index into the array of rocks so that we can find it
                            // to destroy it
                            EntityInfo fullRockData =
                                GridData.getFullHashValue(gridHashMap, (int)rockPos.x, (int)rockPos.y);
                            //Debug.Log("rock task happening : " + rockEntityIndex + " " + tmp.Index);
                            entityIntents[i] = fullRockData;
                            // remove the rock from the hash so that nobody else tries to get it
                            removals.Enqueue(new RemovalInfo {
                                key = key, requestingEntity = entities[i]
                            });
                            break;

                        case Tiles.Till:
                            EntityInfo tillData = new EntityInfo {
                                type = (int)Tiles.Till
                            };
                            entityIntents[i] = tillData;
                            break;

                        case Tiles.Plant:
                            key = GridData.ConvertToHash((int)foundLocation.x, (int)foundLocation.y);
                            removals.Enqueue(new RemovalInfo {
                                key = key, requestingEntity = entities[i]
                            });
                            EntityInfo plantData = new EntityInfo {
                                type = (int)Tiles.Plant
                            };
                            entityIntents[i] = plantData;
                            break;

                        case Tiles.Harvest:
                            key = GridData.ConvertToHash((int)foundLocation.x, (int)foundLocation.y);
                            EntityInfo fullData = GridData.getFullHashValue(gridHashMap, (int)foundLocation.x,
                                                                            (int)foundLocation.y);

                            // check to make sure plant is grown before harvesting
                            // if it's not then find something else to do
                            PlantComponent plantInfo = IsPlantType[fullData.specificEntity];

                            if (plantInfo.timeGrown >= plantGrowthMax)
                            {
                                removals.Enqueue(new RemovalInfo {
                                    key = key, requestingEntity = entities[i]
                                });
                                EntityInfo harvestData = new EntityInfo
                                {
                                    type = (int)Tiles.Harvest, specificEntity = fullData.specificEntity
                                };
                                entityIntents[i] = harvestData;
                                //Debug.Log("plant ready to harvest at : " + fullData.specificEntity.Index + " " + index + " " +
                                //    foundLocation.x + " " + foundLocation.y);
                            }
                            else
                            {
                                // not ready to harvest, try something else
                                // add needs task and remove moving tag
                                addRemoveTags.Enqueue(new TagInfo
                                {
                                    shouldRemove = 0, entity = entities[i], type = Tags.NeedsTask
                                });
                                addRemoveTags.Enqueue(new TagInfo
                                {
                                    shouldRemove = 1, entity = entities[i], type = Tags.Moving
                                });
                            }

                            break;

                        case Tiles.Store:
                            EntityInfo storeInfo = new EntityInfo
                            {
                                type           = (int)Tiles.Store,
                                specificEntity = entityIntents[i].specificEntity
                            };
                            entityIntents[i] = storeInfo;
                            //Debug.Log("plant going to the store " + foundLocation.x + " " + foundLocation.y + " " + entityIntent.specificEntity.Index);
                            break;

                        default:

                            break;
                        }
                    }
                }
                else
                {
                    // no tile was found for the task, so this method
                    // will run again and hopefully pick another task next time
                    // Debug.Log("location wasn't found - find another task");
                }
            }
        }