Commit Description:
Add menu.
Commit Description:
Add menu.
File last commit:
Show/Diff file:
Action:
isometric-park-fna/Simulation.cs
347 lines | 9.5 KiB | text/x-csharp | CSharpLexer
using System;
using System.Collections.Generic;
using static isometricparkfna.CellMap;
using System.Linq;
namespace isometricparkfna
{
public struct Budget
{
public DateTime DateTime;
//assets
public decimal money;
//revenue
public decimal subsidy;
//expenses
public decimal upkeep;
public decimal tree_planting;
public decimal tree_clearing;
public decimal final_money;
public decimal cashflow;
//misc
public int trees;
}
public class Simulation
{
public const int START_YEAR = 2020;
public const int START_MONTH = 1;
public const int START_DAY = 1;
public DateTime START_DATETIME{
get {
return new DateTime(START_YEAR, START_MONTH, START_DAY);
}}
private const float SPONTANEOUS_NEW_TREE_CHANCE = 0.9995f;
private const float NEIGHBOR_NEW_TREE_CHANCE = 0.995f;
private const float NEIGHBOR_CROWDS_TREE_CHANCE = 0.995f;
public const int TREE_PLANT_COST = 750;
public const int TREE_CLEAR_COST = 500;
public const int MAX_TREES_TO_PLANT = 25;
public const int MAX_TREES_TO_CLEAR = 25;
public int Tick
{
get;
private set;
}
public float Elapsed
{
get;
private set;
}
public DateTime DateTime
{
get;
private set;
}
public decimal money;
private List<Budget> budgets;
public Budget latestBudget
{
get
{
if (this.budgets.Count >= 1) {
return this.budgets[this.budgets.Count - 1];
}
else
{
return new Budget { };
}
}
}
public Budget previousBudget
{
get
{
if (this.budgets.Count >= 2) {
return this.budgets[this.budgets.Count - 2];
}
else
{
return new Budget { };
}
}
}
public float[] millisecondsPerAdvance { get; private set; }
public String Season { get
{
if (MathUtils.Between(this.DateTime.Month, 3, 5))
{
return "Spring";
}
else if (MathUtils.Between(this.DateTime.Month, 6, 8))
{
return "Summer";
}
else if (MathUtils.Between(this.DateTime.Month, 9, 11))
{
return "Fall";
}
else
{
return "Winter";
}
}
}
public CellMap map;
public int ticksPerAdvance;
private float lastAdvance;
public bool paused;
public int currentRate;
private Random random;
//forest policy params
private int _tree_planting;
public int tree_planting
{
get {
return _tree_planting;
}
set {
_tree_planting = MathUtils.Clamp(value, 0, MAX_TREES_TO_PLANT);
}
}
private int _tree_clearing = 0;
public int tree_clearing
{
get {
return _tree_clearing;
}
set {
_tree_clearing = MathUtils.Clamp(value, 0, MAX_TREES_TO_CLEAR);
}
}
public int crowded_trees
{
get {
return this.map.iterate_cells_with_neighbors(7).Where(c => c.hasTree).Count();
}
}
public float healthy_percent
{
get {
return (float)(this.map.tree_count - this.map.iterate_cells_with_neighbors(7).Where(c => c.hasTree).Count()) / this.map.tree_count * 100;
}
}
public double average_tree_age
{
get
{
return this.map.iterate_cells().Where(c => c.hasTree).Select(c => (this.DateTime - c.planted).Days / 365.0).Average();
}
}
public double max_tree_age
{
get
{
return this.map.iterate_cells().Where(c => c.hasTree).Select(c => (this.DateTime - c.planted).Days / 365.0).Max();
}
}
public Simulation(int width, int height, float[] millisecondsPerAdvance)
{
this.random = new Random();
this.DateTime = new DateTime(START_YEAR, START_MONTH, START_DAY);
this.map = new CellMap(width, height);
this.money = 100000;
this.millisecondsPerAdvance = millisecondsPerAdvance;
this.paused = true;
this.budgets = new List<Budget>();
}
private void advanceSimulation()
{
this.DateTime = this.DateTime.AddMonths(1);
foreach (Cell cell in this.map.iterate_cells())
{
if (random.NextDouble() > SPONTANEOUS_NEW_TREE_CHANCE)
{
cell.addTree(this.DateTime);
}
}
int new_planted = 0;
foreach (Cell cell in this.map.iterate_cells_with_neighbors(4))
{
if (random.NextDouble() > NEIGHBOR_NEW_TREE_CHANCE)
{
cell.addTree(this.DateTime);
new_planted += 1;
}
}
System.Console.WriteLine(String.Format("New {0}", new_planted));
int crowded_out = 0;
foreach (Cell cell in this.map.iterate_cells_with_neighbors(7))
{
if (random.NextDouble() > NEIGHBOR_CROWDS_TREE_CHANCE)
{
cell.markTreeDead();
crowded_out += 1;
}
}
System.Console.Write(String.Format("Crowded {0}", crowded_out));
int trees_to_plant = this.tree_planting;
while (trees_to_plant > 0 && this.map.remaining_tree_capacity > 0) {
int y = random.Next(0, this.map.MapHeight);
int x = random.Next(0, this.map.MapWidth);
Cell chosen_cell = this.map.cells[x][y];
if (!chosen_cell.hasTree) {
chosen_cell.addTree(this.DateTime);
trees_to_plant -= 1;
}
}
int trees_to_clear = this.tree_clearing;
System.Console.Write(String.Format("Found {0}; ", this.map.iterate_cells_with_neighbors(7).Where(c => c.hasTree).Count()));
foreach (Cell cell in this.map.iterate_cells_with_neighbors(7).Where(c => c.hasTree))
{
if (trees_to_clear > 0) {
cell.removeTree();
trees_to_clear -= 1;
}
}
Budget newBudget = new Budget
{
DateTime = this.DateTime,
money = this.money,
trees = this.map.tree_count,
subsidy = 1000,
upkeep = this.map.tree_count * 1,
tree_planting = this.tree_planting * Simulation.TREE_PLANT_COST,
tree_clearing = this.tree_clearing * Simulation.TREE_CLEAR_COST
};
newBudget = this.applyBudget(newBudget); ;
this.budgets.Add(newBudget);
}
public Budget applyBudget(Budget budget)
{
this.money = budget.money
- (budget.upkeep + budget.tree_planting + budget.tree_clearing)
+ (budget.subsidy);
budget.final_money = this.money;
budget.cashflow = budget.final_money - budget.money;
return budget;
}
public void setRate(int newRate) {
if ((newRate >= 0) && (newRate <= this.millisecondsPerAdvance.Length)) {
this.currentRate = newRate;
}
}
public void update(TimeSpan deltaTime)
{
//this.Tick++;
if (!this.paused)
{
this.Elapsed += deltaTime.Milliseconds;
float millisecondsPerAdvance = this.millisecondsPerAdvance[this.currentRate];
int advancesToSimulate = (int)((this.Elapsed - this.lastAdvance) / millisecondsPerAdvance);
for (int i = 0; i < advancesToSimulate; i++)
{
this.advanceSimulation();
//Partial frames haven't been simulated so they're not counted as part of
//lastAdvance
//Example:
//We start at t=100 and simulate every 10 t. If we miss enough updates that
//it's t=125, we have 2.5 steps to simulate. However, we only want to simulate
//whole steps for simplicity's sake, so that means we'll simulate 2. But that means we've only simulated
//through t=120, so that's what we want to track in lastAdvance.
this.lastAdvance += advancesToSimulate * millisecondsPerAdvance;
}
/*
if ((this.Tick % this.millisecondsPerAdvance) == 0)
{
this.DateTime = this.DateTime.AddMonths(1);
}*/
}
}
}
}