Show More
Commit Description:
Fix handling when there are too few trees....
Commit Description:
Fix handling when there are too few trees. Also add debug tool to remove trees.
Show/Diff file:
Action:
isometric-park-fna/Engines/SimulationBridgeEngine.cs
293 lines | 11.8 KiB | text/x-csharp | CSharpLexer
using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Encompass;
using isometricparkfna.Messages;
using isometricparkfna.Components;
namespace isometricparkfna.Engines
{
[Sends(typeof(SpawnContractMessage), typeof(SpawnDialogMessage), typeof(TickMessage))]
[Reads(typeof(AreaComponent),
typeof(BudgetComponent),
typeof(BudgetLineComponent),
typeof(ContractStatusComponent),
typeof(TreeDeltaComponent),
typeof(PreserveComponent))]
[Writes(typeof(BudgetComponent))]
[Receives(typeof(SingleExpenseMessage), typeof(DebugAlterTreesMessage))]
public class SimulationBridgeEngine : Engine
{
public Simulation simulation;
private int ticksToSend;
private Random random_generator;
Vector2[] all_squares;
public SimulationBridgeEngine(Simulation simulation)
{
this.simulation = simulation;
this.random_generator = new Random();
List<Vector2> squares = new List<Vector2>();
for(int i = 0; i < simulation.map.MapWidth; i++)
{
for(int j = 0; j < simulation.map.MapHeight; j++)
{
squares.Add(new Vector2(i,j));
}
}
this.all_squares = squares.ToArray();
}
public int addTick()
{
this.ticksToSend++;
return ticksToSend;
}
public override void Update(double dt)
{
foreach (ref readonly var message in ReadMessages<SingleExpenseMessage>())
{
this.simulation.AddConstruction(message.amount);
}
foreach (ref readonly var entity in ReadEntities<BudgetComponent>())
{
ref readonly var budgetComponent = ref GetComponent<BudgetComponent>(entity);
SetComponent(entity, new BudgetComponent
{
currentBudget = this.simulation.latestBudget,
priorBudget = this.simulation.previousBudget
});
}
decimal new_contract_amount = 0M;
decimal new_enforcement_amount = 0M;
decimal new_misc_amount = 0M;
foreach (ref readonly var entity in ReadEntities<BudgetLineComponent>())
{
ref readonly var budgetComponent = ref GetComponent<BudgetLineComponent>(entity);
// SetComponent(entity, new BudgetComponent{currentBudget = this.simulation.latestBudget,
// priorBudget = this.simulation.previousBudget});
switch (budgetComponent.category)
{
case "Contracts":
var status = GetComponent<ContractStatusComponent>(entity).status;
if (status == ContractStatus.Accepted)
{
new_contract_amount += budgetComponent.amount;
}
break;
case "Enforcement":
new_enforcement_amount += budgetComponent.amount;
break;
case "Misc":
new_misc_amount += budgetComponent.amount;
break;
}
}
if (this.ticksToSend > 0)
{
Logging.Trace(String.Format("{0} ticks to send in update", this.ticksToSend));
}
if (this.ticksToSend > 1)
{
Logging.Warning(String.Format("Simulating {0} ticks at once!", this.ticksToSend));
}
for (int i = ticksToSend; i > 0; i--)
{
#region calculate_preserve_cells
var preserve_cell_coordinates = new List<(int, int)>();
Stopwatch iterPreserves = new Stopwatch();
iterPreserves.Start();
foreach (ref readonly var entity in ReadEntities<PreserveComponent>()) {
ref readonly var areaComponent = ref GetComponent<AreaComponent>(entity);
foreach (var square in areaComponent.squares) {
preserve_cell_coordinates.Add(((int)square.X, (int)square.Y));
}
}
iterPreserves.Stop();
Logging.Info(String.Format("Preserve entities: {0:F3}", iterPreserves.Elapsed.TotalMilliseconds.ToString()));
Stopwatch iterCells = new Stopwatch();
iterCells.Start();
//This takes about 30ms versus the .0002 ms with no preserve.
//When the map is filled up with preserve, about 35ms and 9ms.
//With a handful of cells, it's more like 0.8ms
/*
for (int j = 0; j < this.simulation.PreserveCounts.GetLength(0); j++) {
for (int k = 0; k < this.simulation.PreserveCounts.GetLength(0); k++) {
count = 0;
foreach (var cell in this.simulation.map.iterate_neighbor_cells(j, k)) {
if (preserve_cells.Contains(cell)) {
count++;
}
}
this.simulation.PreserveCounts[j, k] = count;
}
}
//*/
//*
foreach ((var x, var y) in preserve_cell_coordinates) {
foreach ((var newx, var newy) in this.simulation.map.iterate_neighbor_cell_locations(x, y)) {
this.simulation.PreserveCounts[newx, newy] += 1;
}
}
//*/
iterCells.Stop();
Logging.Info(String.Format("Cell loop: {0:F3}", iterCells.Elapsed.TotalMilliseconds.ToString()));
#endregion
SendMessage<TickMessage>(new TickMessage { SimulationDateTime = simulation.DateTime });
//For now:
SendMessage<SpawnContractMessage>(new SpawnContractMessage {name = string.Format("#logging_company.capitalizeAll# {0}",
this.simulation.DateTime.ToShortDateString()) });
#region events
if (simulation.latestBudget.DateTime != default)
{
var tree_count_threshold = (int)(0.05 * this.simulation.map.tree_capacity);
if (simulation.latestBudget.money < 0M
&& simulation.previousBudget.money >= 0M)
{
SendMessage<SpawnDialogMessage>(new SpawnDialogMessage {Path = "EndLowFunds"});
}
else if (simulation.latestBudget.money < 10_000M
&& simulation.previousBudget.money >= 10_000M)
{
SendMessage<SpawnDialogMessage>(new SpawnDialogMessage {Path = "VeryLowFunds"});
}
else if (simulation.latestBudget.money < 25_000M
&& simulation.previousBudget.money >= 25_000M)
{
SendMessage<SpawnDialogMessage>(new SpawnDialogMessage {Path = "LowFunds"});
}
if ((simulation.latestBudget.trees < tree_count_threshold)
&& (simulation.previousBudget.trees >= tree_count_threshold))
{
SendMessage<SpawnDialogMessage>(new SpawnDialogMessage {Path = "PoorTreeHealth"});
}
Logging.Spy(new {count = preserve_cell_coordinates.Count(), half = (simulation.map.tree_capacity * 0.5)} );
if ((simulation.latestBudget.preserve_cells > (simulation.map.tree_capacity * 0.5))
&& (simulation.previousBudget.preserve_cells <= (simulation.map.tree_capacity * 0.5)))
{
SendMessage<SpawnDialogMessage>(new SpawnDialogMessage {Path = "PreserveHalf"});
var fundraiserEntity = CreateEntity();
AddComponent(fundraiserEntity, new BudgetLineComponent {
category = "Misc",
amount = 20_000M });
}
else if ((simulation.latestBudget.preserve_cells > (simulation.map.tree_capacity * 0.25))
&& (simulation.previousBudget.preserve_cells <= (simulation.map.tree_capacity * 0.25)))
{
SendMessage<SpawnDialogMessage>(new SpawnDialogMessage {Path = "PreserveProgress"});
}
}
#endregion
foreach (ref readonly var entity in ReadEntities<TreeDeltaComponent>())
{
if ((HasComponent<ContractStatusComponent>(entity) && GetComponent<ContractStatusComponent>(entity).status == ContractStatus.Accepted)
|| !(HasComponent<ContractStatusComponent>(entity) ))
{
var delta = GetComponent<TreeDeltaComponent>(entity);
var squares = HasComponent<AreaComponent>(entity) ? GetComponent<AreaComponent>(entity).squares : this.all_squares;
var removed = 0;
var added = 0;
var tree_squares = squares.Where((square) => simulation.map.cells[(int)square.X][(int)square.Y].HasTree);
var to_remove = Math.Abs(delta.deltaTrees.Value);
//calculate the probability in order to get the expected number of removed trees
var expected = to_remove;
var trials = tree_squares.Count();
double probability = ((double)expected / (double)trials) / 12;
foreach (var square in tree_squares)
{
var cell = simulation.map.cells[(int)square.X][(int)square.Y];
if (cell.HasTree
&& random_generator.NextDouble() < probability)
{
if (delta.deltaTrees.Value < 0)
{
cell.RemoveTree();
removed++;
}
else if (delta.deltaTrees.Value > 0)
{
var random_type = random_generator.Next(0, 3);
cell.AddTree(this.simulation.DateTime, (CellMap.TreeType)random_type);
added++;
}
}
if (removed >= to_remove)
{
// break;
}
}
Logging.Info(String.Format("Destroyed {0} trees, expected {1}, P(plant)= {2}", removed, (expected / 12.0), probability));
Logging.Info(String.Format("Planted {0} trees, expected {1}, P(destroy)= {2}", added, (expected / 12.0), probability));
}
}
simulation.preserve_cells = preserve_cell_coordinates.Count();
}
this.ticksToSend = 0;
simulation.contracts = new_contract_amount;
simulation.enforcement = new_enforcement_amount;
simulation.misc = new_misc_amount;
#region debug
foreach (ref readonly var message in ReadMessages<DebugAlterTreesMessage>())
{
var trees = message.DeltaTrees;
//Probaby not very performant; used only for debug.
foreach (CellMap.Cell cell in this.simulation.map.iterate_cells().Where(c => c.HasTree).ToList().Shuffle())
{
if (trees > 0) {
trees -= 1;
cell.RemoveTree();
}
}
}
#endregion
//Move this here?
// this.simulation.update(dt);
}
}
}