Commit Description:
Refactor out more code into InputEngine.
Commit Description:
Refactor out more code into InputEngine.
File last commit:
Show/Diff file:
Action:
isometric-park-fna/Simulation.cs
373 lines | 10.2 KiB | text/x-csharp | CSharpLexer
using System;
using System.Collections.Generic;
using static isometricparkfna.CellMap;
using System.Linq;
using isometricparkfna.UI; //purely for news item
using isometricparkfna.Engines;
using TraceryNet;
namespace isometricparkfna
{
public struct Budget
{
public DateTime DateTime;
//assets
public decimal money;
//revenue
public decimal subsidy;
public decimal contracts;
//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 = 500;
public const int TREE_CLEAR_COST = 250;
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 SimulationBridgeEngine BridgeEngine { get; private set; }
// public SimulationBridgeEngine bridgeEngine {get;}
public decimal contracts;
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 { };
}
}
}
private Grammar grammar;
private List<NewsItem> sourceNewsItems;
public List<NewsItem> latestNewsItems;
public float[] millisecondsPerAdvance { get; private set; }
public String Season { get
{
if (MathUtils.BetweenInclusive(this.DateTime.Month, 3, 5))
{
return "Spring";
}
else if (MathUtils.BetweenInclusive(this.DateTime.Month, 6, 8))
{
return "Summer";
}
else if (MathUtils.BetweenInclusive(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>();
this.BridgeEngine = new SimulationBridgeEngine(this);
}
private void advanceSimulation()
{
var oldSeason = this.Season;
this.DateTime = this.DateTime.AddMonths(1);
var newSeason = this.Season;
var seasonChanged = oldSeason != newSeason;
this.BridgeEngine.addTick();
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;
}
}
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;
}
}
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;
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,
contracts = this.contracts,
upkeep = (int)(this.map.tree_count * 1.5),
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);
if (seasonChanged) {
this.updateNews();
}
}
public Budget applyBudget(Budget budget)
{
this.money = budget.money
- (budget.upkeep + budget.tree_planting + budget.tree_clearing)
+ (budget.subsidy + budget.contracts);
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 updateNews() {
this.latestNewsItems = this.sourceNewsItems.Select(s => s.Flatten(this.grammar)).ToList().Shuffle();
}
public void LoadContent(List<NewsItem> sourceNewsItems, Grammar grammar) {
this.sourceNewsItems = sourceNewsItems;
this.grammar = grammar;
this.updateNews();
}
public void update(TimeSpan deltaTime)
{
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;
}
}
}
}
}