153 changed files with 5035 additions and 2084 deletions
@ -0,0 +1,393 @@
|
||||
@layout PageLayout |
||||
|
||||
@inject IDataCollectionService DataCollectionService |
||||
@using Model |
||||
@inherits BasePage |
||||
|
||||
@page "/data-tables" |
||||
|
||||
<LayoutMediumContentComponent> |
||||
<WebsiteTitleComponent>Comparision Tables</WebsiteTitleComponent> |
||||
|
||||
<PaperComponent> |
||||
Credit to Zard for deriving the formula. |
||||
</PaperComponent> |
||||
|
||||
<PaperComponent> |
||||
<LayoutRowComponent> |
||||
<LayoutColumnComponent> |
||||
<FormLayoutComponent> |
||||
<FormDisplayComponent Label="Cost of worker"> |
||||
<Display>@CostOfWorker</Display> |
||||
</FormDisplayComponent> |
||||
|
||||
<FormDisplayComponent Label="Alloy mined per second by worker"> |
||||
<Display>@AlloyMinedPerSecondByWorker</Display> |
||||
</FormDisplayComponent> |
||||
|
||||
<FormDisplayComponent Label="Time to produce worker"> |
||||
<Display>@TimeToProduceWorker</Display> |
||||
</FormDisplayComponent> |
||||
</FormLayoutComponent> |
||||
</LayoutColumnComponent> |
||||
|
||||
<LayoutColumnComponent> |
||||
<FormLayoutComponent> |
||||
<FormNumberComponent Min="1" |
||||
Id="numberOfWorkersLostToHarass" |
||||
Value="@((int)NumberOfWorkersLostToHarass)" |
||||
OnChange="@(e => |
||||
{ |
||||
NumberOfWorkersLostToHarass = int.Parse(e.Value!.ToString()!); |
||||
Calculate(); |
||||
})"> |
||||
<FormLabelComponent>Number of workers lost to harass</FormLabelComponent> |
||||
</FormNumberComponent> |
||||
|
||||
<FormNumberComponent Min="1" |
||||
Id="numberOfTownHallsExisting" |
||||
Value="@((int)NumberOfTownHallsExisting)" |
||||
OnChange="OnTownHallsChanged"> |
||||
<FormLabelComponent>Number of townhalls you have</FormLabelComponent> |
||||
</FormNumberComponent> |
||||
|
||||
<div id="numberOfTownHallTravelTimes"> |
||||
@{ |
||||
var index = 0; |
||||
} |
||||
@foreach (var travelTime in TravelTimes) |
||||
{ |
||||
index++; |
||||
if (index == 1) |
||||
{ |
||||
continue; |
||||
} |
||||
|
||||
var id = $"numberOfTownHallsExisting_{index}"; |
||||
<FormNumberComponent Min="0" |
||||
Id="@id" |
||||
Value="@((int)travelTime.Value)" |
||||
OnChange="e => { OnTownHallTravelTimeChanged(e, travelTime); }"> |
||||
<FormLabelComponent>Worker travel time from other |
||||
base @(travelTime.Index + 1)</FormLabelComponent> |
||||
</FormNumberComponent> |
||||
} |
||||
</div> |
||||
|
||||
|
||||
<FormDisplayComponent Label="Total alloy lost"> |
||||
<Display> |
||||
<div style="font-size: 1.5rem; font-weight: 800;"> |
||||
<span id="totalAlloyHarassment"> |
||||
@TotalAlloyHarassment |
||||
</span> |
||||
</div> |
||||
</Display> |
||||
</FormDisplayComponent> |
||||
|
||||
|
||||
</FormLayoutComponent> |
||||
|
||||
<br/> |
||||
<div> |
||||
(<b>Worker replacement costs:</b> <span id="workerReplacementCost">@WorkerReplacementCost()</span>) |
||||
</div> |
||||
<div> |
||||
(<b>Delayed mining time:</b> <span id="delayedMiningCost">@DelayedMiningCost()</span>) |
||||
</div> |
||||
<div> |
||||
(<b>Average travel time:</b> <span id="getAverageTravelTime">@GetAverageTravelTime()</span>) |
||||
</div> |
||||
|
||||
</LayoutColumnComponent> |
||||
</LayoutRowComponent> |
||||
</PaperComponent> |
||||
|
||||
<ContentDividerComponent></ContentDividerComponent> |
||||
|
||||
<PaperComponent> |
||||
<InfoBodyComponent> |
||||
<InfoQuestionComponent> |
||||
What is this tool? |
||||
</InfoQuestionComponent> |
||||
<InfoAnswerComponent> |
||||
The Harass Calculator allows you to calculate damage done to an enemy alloy line. For example, if you |
||||
were to attack with Ichors, and kill 6 enemy workers, you can set the |
||||
<b> |
||||
Number of workers lost to |
||||
harass |
||||
</b> to 6. This would determine a loss of <span id="exampleTotalAlloyLoss">@ExampleTotalAlloyLoss</span> |
||||
alloy. Quite |
||||
the large number. |
||||
</InfoAnswerComponent> |
||||
</InfoBodyComponent> |
||||
|
||||
<InfoBodyComponent> |
||||
<InfoQuestionComponent> |
||||
What can I learn from this? |
||||
</InfoQuestionComponent> |
||||
<InfoAnswerComponent> |
||||
Well, let's assume you lost a full alloy line of workers, and have to take that |
||||
@ExampleTotalAlloyLoss alloy cost (<span id="exampleWorkerCost">@ExampleWorkerCost</span> |
||||
to rebuy the workers, and <span id="exampleMiningTimeCost">@ExampleMiningTimeCost</span> in lost mining |
||||
time.) |
||||
<br/><br/> |
||||
If you were to set the <b>Number of townhalls you have</b> to 2, the calculator will consider worker |
||||
transfer micro. Allowing you to cut the total cost by roughly |
||||
<span id="exampleTotalAlloyLossDifference">@ExampleTotalAlloyLossDifference</span> alloy. However, that |
||||
number isn't |
||||
entirely accurate, you are also going to have to bump up the <b>Worker travel time to alloy</b> to |
||||
account for the time it takes the transferred workers to arrive at the decimated alloy line. |
||||
<br/><br/> |
||||
Let's say it takes 10 seconds for workers to transfer from your second base. Let's enter that for the |
||||
second base travel time for the more accurate loss of |
||||
<span |
||||
id="exampleTotalAlloyLossAccurate"> |
||||
@ExampleTotalAlloyLossAccurate |
||||
</span> alloy |
||||
(saving you <span |
||||
id="exampleTotalAlloyLossAccurateDifference">@ExampleTotalAlloyLossAccurateDifference</span> alloy.) |
||||
<i> |
||||
Which is |
||||
much better than not transferring workers! |
||||
</i> |
||||
</InfoAnswerComponent> |
||||
</InfoBodyComponent> |
||||
|
||||
<InfoBodyComponent> |
||||
<InfoQuestionComponent> |
||||
Can I see the formula for the calculation? |
||||
</InfoQuestionComponent> |
||||
<InfoAnswerComponent> |
||||
The Harass Calculator is based on the following calculation. |
||||
|
||||
<br/> |
||||
<div class="mathContainer"> |
||||
<div> =c*m+r*g*(t+l) +</div> |
||||
|
||||
<MathLoopSumComponent> |
||||
<LoopStart><i>x</i> =1</LoopStart> |
||||
<LoopEnd> |
||||
⌊ |
||||
<MathDivisionComponent> |
||||
<Dividee>m</Dividee> |
||||
<Divider>a</Divider> |
||||
</MathDivisionComponent> |
||||
⌋ |
||||
</LoopEnd> |
||||
<IndexSymbol> |
||||
<i>x</i> |
||||
</IndexSymbol> |
||||
</MathLoopSumComponent> |
||||
|
||||
<div style="width: 132px">g*<i>x</i>*(t+l)</div> |
||||
</div> |
||||
<br/> |
||||
<div style="font-family:monospace;"> |
||||
<div>c is CostOfWorker</div> |
||||
<div>m is NumberOfWorkersLostToHarass, <i>m for [M]otes</i></div> |
||||
<div>a is NumberOfTownHallsExisting, <i>a for [A]cropolis</i></div> |
||||
<div>r is m mod a is LeftOverWorkersToProduceCount()</div> |
||||
<div>g is AlloyMinedPerSecondByWorker</div> |
||||
<div>t is TimeToProduceWorker</div> |
||||
<div>l is TravelTime</div> |
||||
<div><i>x</i> is workerProductionIndex</div> |
||||
</div> |
||||
<br/> |
||||
This logic has since been changed slightly to allow client to enter different travel times per base. |
||||
<br/> |
||||
</InfoAnswerComponent> |
||||
</InfoBodyComponent> |
||||
<InfoBodyComponent> |
||||
<InfoQuestionComponent> |
||||
Can I see the code for the calculation? |
||||
</InfoQuestionComponent> |
||||
<InfoAnswerComponent> |
||||
<br/> |
||||
<LinkButtonComponent |
||||
Href="https://git.jonathanmccaffrey.ca/JonathanMcCaffrey/IGP-Fan-Reference/src/branch/main/IGP/Pages/HarassCalculatorPage.razor#L226"> |
||||
View Here |
||||
</LinkButtonComponent> |
||||
</InfoAnswerComponent> |
||||
</InfoBodyComponent> |
||||
</PaperComponent> |
||||
|
||||
|
||||
</LayoutMediumContentComponent> |
||||
|
||||
<style> |
||||
.mathContainer { |
||||
font-family: monospace; |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: flex-start; |
||||
border: 1px black solid; |
||||
padding: 32px; |
||||
background-color: #1A1B1E |
||||
} |
||||
|
||||
@@media only screen and (max-width: 1025px) { |
||||
.mathContainer { |
||||
padding-left: 2px; |
||||
padding-right: 2px; |
||||
} |
||||
} |
||||
</style> |
||||
|
||||
@code { |
||||
|
||||
// Example calcs |
||||
float ExampleTotalAlloyLoss => Calculate( |
||||
WorkerReplacementCost(6), |
||||
SimultaneousProductionFloor(1, 6), |
||||
6, |
||||
new List<float> { 0 }); |
||||
|
||||
float ExampleWorkerCost => WorkerReplacementCost(6); |
||||
|
||||
float ExampleMiningTimeCost => ExampleTotalAlloyLoss - ExampleWorkerCost; |
||||
|
||||
float ExampleTotalAlloyLossDifference => ExampleTotalAlloyLoss - Calculate( |
||||
WorkerReplacementCost(6), |
||||
SimultaneousProductionFloor(2, 6), |
||||
6, |
||||
new List<float> { 0, 0 }); |
||||
|
||||
float ExampleTotalAlloyLossAccurate => Calculate( |
||||
WorkerReplacementCost(6), |
||||
SimultaneousProductionFloor(2, 6), |
||||
6, |
||||
new List<float> { 0, 10 }); |
||||
|
||||
float ExampleTotalAlloyLossAccurateDifference => ExampleTotalAlloyLoss - ExampleTotalAlloyLossAccurate; |
||||
|
||||
float TotalAlloyHarassment; |
||||
|
||||
readonly float CostOfWorker = 50; |
||||
readonly float AlloyMinedPerSecondByWorker = 1; |
||||
readonly float TimeToProduceWorker = 20; |
||||
float NumberOfWorkersLostToHarass = 1; |
||||
float NumberOfTownHallsExisting = 1; |
||||
|
||||
float GetAverageTravelTime() |
||||
{ |
||||
if (TravelTimes.Count == 0) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
float sum = 0; |
||||
|
||||
foreach (var travelTime in TravelTimes) |
||||
{ |
||||
sum += travelTime.Value; |
||||
} |
||||
|
||||
return sum / NumberOfTownHallsExisting; |
||||
} |
||||
|
||||
|
||||
float SimultaneousProductionFloor() |
||||
{ |
||||
if (NumberOfTownHallsExisting <= 0 || NumberOfWorkersLostToHarass <= 0) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
return NumberOfWorkersLostToHarass / Math.Min(NumberOfTownHallsExisting, NumberOfWorkersLostToHarass); |
||||
} |
||||
|
||||
float SimultaneousProductionFloor(float existingTownHalls, float numberOfWorkersLost) |
||||
{ |
||||
if (existingTownHalls <= 0 || numberOfWorkersLost <= 0) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
return numberOfWorkersLost / Math.Min(existingTownHalls, numberOfWorkersLost); |
||||
} |
||||
|
||||
float WorkerReplacementCost() |
||||
{ |
||||
return CostOfWorker * NumberOfWorkersLostToHarass; |
||||
} |
||||
|
||||
float WorkerReplacementCost(int numberOfWorkersLostToHarass) |
||||
{ |
||||
return CostOfWorker * numberOfWorkersLostToHarass; |
||||
} |
||||
|
||||
float DelayedMiningCost() |
||||
{ |
||||
return TotalAlloyHarassment - WorkerReplacementCost(); |
||||
} |
||||
|
||||
void Calculate() |
||||
{ |
||||
TotalAlloyHarassment = Calculate(WorkerReplacementCost(), |
||||
SimultaneousProductionFloor(), |
||||
NumberOfWorkersLostToHarass, |
||||
TravelTimes.Select(x => x.Value).ToList(), |
||||
TimeToProduceWorker, |
||||
AlloyMinedPerSecondByWorker); |
||||
} |
||||
|
||||
float Calculate(float workerReplacementCost, |
||||
float simultaneousProductionFloor, |
||||
float numberOfWorkersLostToHarass, |
||||
IList<float> travelTimes, |
||||
float timeToProduceWorker = 20, |
||||
float alloyMinedPerSecondByWorker = 1) |
||||
{ |
||||
var totalAlloyHarassment = workerReplacementCost; |
||||
|
||||
for (var workerProductionIndex = 0; workerProductionIndex < simultaneousProductionFloor; workerProductionIndex++) |
||||
{ |
||||
totalAlloyHarassment += alloyMinedPerSecondByWorker * timeToProduceWorker * (workerProductionIndex + 1); |
||||
} |
||||
|
||||
var remainder = (int)(numberOfWorkersLostToHarass % simultaneousProductionFloor); |
||||
for (var remainderIndex = 0; remainderIndex < remainder; remainderIndex++) |
||||
{ |
||||
totalAlloyHarassment += alloyMinedPerSecondByWorker * timeToProduceWorker * (simultaneousProductionFloor + 1); |
||||
} |
||||
|
||||
for (var travelTimeIndex = 0; travelTimeIndex < numberOfWorkersLostToHarass; travelTimeIndex++) |
||||
{ |
||||
var townHallIndex = travelTimeIndex % travelTimes.Count; |
||||
totalAlloyHarassment += alloyMinedPerSecondByWorker * travelTimes[townHallIndex]; |
||||
} |
||||
|
||||
return totalAlloyHarassment; |
||||
} |
||||
|
||||
protected override void OnInitialized() |
||||
{ |
||||
base.OnInitialized(); |
||||
Calculate(); |
||||
} |
||||
|
||||
|
||||
public List<TravelTime> TravelTimes { get; set; } = new() { new TravelTime(0, 0) }; |
||||
|
||||
private void OnTownHallsChanged(ChangeEventArgs obj) |
||||
{ |
||||
NumberOfTownHallsExisting = int.Parse(obj.Value!.ToString()!); |
||||
|
||||
while (TravelTimes.Count > NumberOfTownHallsExisting) |
||||
TravelTimes.Remove(TravelTimes.Last()); |
||||
|
||||
while (TravelTimes.Count < NumberOfTownHallsExisting) |
||||
TravelTimes.Add(new TravelTime(TravelTimes.Count, 10 * TravelTimes.Count)); |
||||
Calculate(); |
||||
} |
||||
|
||||
private void OnTownHallTravelTimeChanged(ChangeEventArgs obj, TravelTime travelTime) |
||||
{ |
||||
travelTime.Value = (int)obj.Value!; |
||||
|
||||
Calculate(); |
||||
StateHasChanged(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,5 @@
|
||||
<h3>UnitTable</h3> |
||||
|
||||
@code { |
||||
|
||||
} |
||||
@ -0,0 +1,23 @@
|
||||
<h3>WeaponTable</h3> |
||||
|
||||
|
||||
<MudDataGrid Items="@_entityWeapons.Take(4)"> |
||||
<Columns> |
||||
<PropertyColumn Property="x => x.Range" Title="Nr"/> |
||||
</Columns> |
||||
</MudDataGrid> |
||||
|
||||
@code { |
||||
List<EntityWeaponModel> _entityWeapons; |
||||
|
||||
protected override void OnInitialized() |
||||
{ |
||||
base.OnInitialized(); |
||||
|
||||
_entityWeapons = DATA.Get() |
||||
.SelectMany(e => e.Value.EntityParts) |
||||
.OfType<EntityWeaponModel>() |
||||
.ToList(); |
||||
} |
||||
|
||||
} |
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue