Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should not prefer inaccessible recipes: Apply cost penalty #257

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions Yafc.Model/Analysis/Analysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ public static float ApproximateFlow(this FactorioObject recipe, bool atCurrentMi
return CostAnalysis.Get(atCurrentMilestones).flow[recipe];
}

public static float ProductCost(this Recipe recipe, bool atCurrentMilestones = false) {
return CostAnalysis.Get(atCurrentMilestones).recipeProductCost[recipe];
}

public static float RecipeWaste(this Recipe recipe, bool atCurrentMilestones = false) {
return CostAnalysis.Get(atCurrentMilestones).recipeWastePercentage[recipe];
}
Expand Down
21 changes: 13 additions & 8 deletions Yafc.Model/Analysis/CostAnalysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,17 @@ public static CostAnalysis Get(bool atCurrentMilestones) {

public Mapping<FactorioObject, float> cost;
public Mapping<Recipe, float> recipeCost;
public Mapping<RecipeOrTechnology, float> recipeProductCost;
public Mapping<FactorioObject, float> flow;
public Mapping<Recipe, float> recipeWastePercentage;
public Goods[]? importantItems;
private readonly bool onlyCurrentMilestones = onlyCurrentMilestones;
private string? itemAmountPrefix;

private bool ShouldInclude(FactorioObject obj) {
// Consider all recipes
if (obj is Recipe) {
return true;
}
return onlyCurrentMilestones ? obj.IsAutomatableWithCurrentMilestones() : obj.IsAutomatable();
}

Expand Down Expand Up @@ -103,7 +106,6 @@ public override void Compute(Project project, ErrorCollector warnings) {
}

var export = Database.objects.CreateMapping<float>();
var recipeProductionCost = Database.recipesAndTechnologies.CreateMapping<float>();
recipeCost = Database.recipes.CreateMapping<float>();
flow = Database.objects.CreateMapping<float>();
var lastVariable = Database.goods.CreateMapping<Variable>();
Expand Down Expand Up @@ -230,6 +232,12 @@ public override void Compute(Project project, ErrorCollector warnings) {
constraint.SetUb(logisticsCost);
export[recipe] = logisticsCost;
recipeCost[recipe] = logisticsCost;

// export controls the value shown by the "There are better recipes to create X (wasting Y% of YAFC cost)" string.
// recipeCost controls the decisions made by the solver. We only want to affect the solver.
if (onlyCurrentMilestones ? !recipe.IsAccessibleWithCurrentMilestones() : !recipe.IsAccessible()) {
recipeCost[recipe] *= project.settings.InaccessibleRecipePenalty;
}
}

// TODO this is temporary fix for strange item sources (make the cost of item not higher than the cost of its source)
Expand Down Expand Up @@ -298,11 +306,9 @@ public override void Compute(Project project, ErrorCollector warnings) {
if (o is RecipeOrTechnology recipe) {
foreach (var ingredient in recipe.ingredients) // TODO split
{
export[o] += export[ingredient.goods] * ingredient.amount;
}

foreach (var product in recipe.products) {
recipeProductionCost[recipe] += product.amount * export[product.goods];
if (float.IsFinite(export[ingredient.goods])) {
export[o] += export[ingredient.goods] * ingredient.amount;
}
}
}
else if (o is Entity entity) {
Expand All @@ -316,7 +322,6 @@ public override void Compute(Project project, ErrorCollector warnings) {
}
}
cost = export;
recipeProductCost = recipeProductionCost;

recipeWastePercentage = Database.recipes.CreateMapping<float>();
if (result is Solver.ResultStatus.OPTIMAL or Solver.ResultStatus.FEASIBLE) {
Expand Down
12 changes: 6 additions & 6 deletions Yafc.Model/Model/ProductionTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -468,10 +468,10 @@ private static void AddLinkCoef(Constraint cst, Variable var, ProductionLink lin
RecipeRow? ownerRecipe = link.owner.owner as RecipeRow;
while (ownerRecipe != null) {
if (link.notMatchedFlow > 0f) {
ownerRecipe.parameters.warningFlags |= WarningFlags.OverproductionRequired;
ownerRecipe.parameters = ownerRecipe.parameters.WithFlag(WarningFlags.OverproductionRequired);
}
else {
ownerRecipe.parameters.warningFlags |= WarningFlags.DeadlockCandidate;
ownerRecipe.parameters = ownerRecipe.parameters.WithFlag(WarningFlags.DeadlockCandidate);
}

ownerRecipe = ownerRecipe.owner.owner as RecipeRow;
Expand All @@ -483,10 +483,10 @@ private static void AddLinkCoef(Constraint cst, Variable var, ProductionLink lin
foreach (var link in linkList) {
if (link.flags.HasFlags(ProductionLink.Flags.LinkRecursiveNotMatched)) {
if (link.notMatchedFlow > 0f) {
recipe.parameters.warningFlags |= WarningFlags.OverproductionRequired;
recipe.parameters = recipe.parameters.WithFlag(WarningFlags.OverproductionRequired);
}
else {
recipe.parameters.warningFlags |= WarningFlags.DeadlockCandidate;
recipe.parameters = recipe.parameters.WithFlag(WarningFlags.DeadlockCandidate);
}
}
}
Expand Down Expand Up @@ -544,12 +544,12 @@ private bool CheckBuiltCountExceeded() {
for (int i = 0; i < recipes.Count; i++) {
var recipe = recipes[i];
if (recipe.buildingCount > recipe.builtBuildings) {
recipe.parameters.warningFlags |= WarningFlags.ExceedsBuiltCount;
recipe.parameters = recipe.parameters.WithFlag(WarningFlags.ExceedsBuiltCount);
builtCountExceeded = true;
}
else if (recipe.subgroup != null) {
if (recipe.subgroup.CheckBuiltCountExceeded()) {
recipe.parameters.warningFlags |= WarningFlags.ExceedsBuiltCount;
recipe.parameters = recipe.parameters.WithFlag(WarningFlags.ExceedsBuiltCount);
builtCountExceeded = true;
}
}
Expand Down
1 change: 1 addition & 0 deletions Yafc.Model/Model/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ public class ProjectSettings(Project project) : ModelObject<Project>(project) {
public int reactorSizeX { get; set; } = 2;
public int reactorSizeY { get; set; } = 2;
public float PollutionCostModifier { get; set; } = 0;
public float InaccessibleRecipePenalty { get; set; } = 100;
public event Action<bool>? changed;
protected internal override void ThisChanged(bool visualOnly) {
changed?.Invoke(visualOnly);
Expand Down
10 changes: 3 additions & 7 deletions Yafc.Model/Model/RecipeParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,8 @@ public struct UsedModule {
public int beaconCount;
}

internal class RecipeParameters(float recipeTime, float fuelUsagePerSecondPerBuilding, float productivity, WarningFlags warningFlags, ModuleEffects activeEffects, UsedModule modules) {
internal sealed record RecipeParameters(float recipeTime, float fuelUsagePerSecondPerBuilding, float productivity, WarningFlags warningFlags, ModuleEffects activeEffects, UsedModule modules) {
public const float MIN_RECIPE_TIME = 1f / 60;
public float recipeTime { get; } = recipeTime;
public float fuelUsagePerSecondPerBuilding { get; } = fuelUsagePerSecondPerBuilding;
public float productivity { get; } = productivity;
public WarningFlags warningFlags { get; internal set; } = warningFlags;
public ModuleEffects activeEffects { get; } = activeEffects;
public UsedModule modules { get; } = modules;

public static RecipeParameters Empty = new(0, 0, 0, 0, default, default);

Expand Down Expand Up @@ -182,5 +176,7 @@ public static RecipeParameters CalculateParameters(RecipeRow row) {

return new RecipeParameters(recipeTime, fuelUsagePerSecondPerBuilding, productivity, warningFlags, activeEffects, modules);
}

internal RecipeParameters WithFlag(WarningFlags additionalFlag) => this with { warningFlags = warningFlags | additionalFlag };
}
}
5 changes: 4 additions & 1 deletion Yafc.UI/Core/ExceptionScreen.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Threading.Tasks;
using SDL2;
using Serilog;

Expand Down Expand Up @@ -33,7 +34,7 @@ protected internal override void Close() {
exists = false;
}

protected override void BuildContents(ImGui gui) {
protected override Task BuildContents(ImGui gui) {
gui.BuildText(ex.GetType().Name, Font.header);
gui.BuildText(ex.Message, new TextBlockDisplayStyle(Font.subheader, true));
gui.BuildText(ex.StackTrace, TextBlockDisplayStyle.WrappedText);
Expand All @@ -50,6 +51,8 @@ protected override void BuildContents(ImGui gui) {
_ = SDL.SDL_SetClipboardText(ex.Message + "\n\n" + ex.StackTrace);
}
}

return Task.CompletedTask;
}
}
}
7 changes: 4 additions & 3 deletions Yafc.UI/Core/Window.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Numerics;
using System.Threading.Tasks;
using SDL2;
using Serilog;

Expand Down Expand Up @@ -201,12 +202,12 @@ public void ShowDropDown(ImGui targetGui, Rect target, GuiBuilder builder, Paddi
ShowDropDown(simpleDropDown);
}

private void Build(ImGui gui) {
private async void Build(ImGui gui) {
if (closed) {
return;
}

BuildContents(gui);
await BuildContents(gui);
if (dropDown != null) {
dropDown.Build(gui);
if (!dropDown.active) {
Expand All @@ -222,7 +223,7 @@ private void Build(ImGui gui) {
}
}

protected abstract void BuildContents(ImGui gui);
protected abstract Task BuildContents(ImGui gui);
public virtual void Dispose() => rootGui.Dispose();

internal ImGui.DragOverlay GetDragOverlay() => draggingOverlay ??= new ImGui.DragOverlay();
Expand Down
7 changes: 4 additions & 3 deletions Yafc.UI/Core/WindowMain.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using SDL2;
using Serilog;

Expand Down Expand Up @@ -35,12 +36,12 @@ protected void Create(string title, int display, float initialWidth, float initi
base.Create();
}

protected override void BuildContents(ImGui gui) {
BuildContent(gui);
protected override async Task BuildContents(ImGui gui) {
await BuildContent(gui);
gui.SetContextRect(new Rect(default, size));
}

protected abstract void BuildContent(ImGui gui);
protected abstract Task BuildContent(ImGui gui);

protected override void OnRepaint() {
rootGui.Rebuild();
Expand Down
3 changes: 3 additions & 0 deletions Yafc.UI/ImGui/ImGuiTextInputHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ public bool BuildTextInput(string? text, out string newText, string? placeholder
if (displayStyle.Icon != Icon.None) {
gui.BuildIcon(displayStyle.Icon, lineSize, (SchemeColor)displayStyle.ColorGroup + 3);
}
if (displayStyle.Prefix != null) {
gui.BuildText(displayStyle.Prefix);
}

textRect = gui.RemainingRow(0.3f).AllocateRect(0, lineSize, RectAlignment.MiddleFullRow);
}
Expand Down
3 changes: 2 additions & 1 deletion Yafc.UI/ImGui/TextDisplayStyles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public record TextBlockDisplayStyle(Font? Font = null, bool WrapText = false, Re
/// <param name="Padding">The <see cref="UI.Padding"/> to place between the text and the edges of the editable area. (The box area not used by <paramref name="Icon"/>.)</param>
/// <param name="Alignment">The <see cref="RectAlignment"/> to apply when drawing the text within the edit box.</param>
/// <param name="ColorGroup">The <see cref="SchemeColorGroup"/> to use when drawing the edit box.</param>
public record TextBoxDisplayStyle(Icon Icon, Padding Padding, RectAlignment Alignment, SchemeColorGroup ColorGroup) {
/// <param name="Prefix">The text to display inside the textbox, before the value.</param>
public record TextBoxDisplayStyle(Icon Icon, Padding Padding, RectAlignment Alignment, SchemeColorGroup ColorGroup, string? Prefix = null) {
/// <summary>
/// Gets the default display style, used for the Preferences screen and calls to <see cref="ImGui.BuildTextInput(string?, out string, string?, Icon, bool, bool)"/>.
/// </summary>
Expand Down
7 changes: 5 additions & 2 deletions Yafc/Windows/AboutScreen.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using Yafc.UI;
using System.Threading.Tasks;
using Yafc.UI;

namespace Yafc {
public class AboutScreen : WindowUtility {
public const string Github = "https://github.com/have-fun-was-taken/yafc-ce";

public AboutScreen(Window parent) : base(ImGuiUtils.DefaultScreenPadding) => Create("About YAFC-CE", 50, parent);

protected override void BuildContents(ImGui gui) {
protected override Task BuildContents(ImGui gui) {
gui.allocator = RectAllocator.Center;
gui.BuildText("Yet Another Factorio Calculator", new TextBlockDisplayStyle(Font.header, Alignment: RectAlignment.Middle));
gui.BuildText("(Community Edition)", TextBlockDisplayStyle.Centered);
Expand Down Expand Up @@ -66,6 +67,8 @@ protected override void BuildContents(ImGui gui) {
gui.allocator = RectAllocator.Center;
gui.BuildText("Factorio name, content and materials are trademarks and copyrights of Wube Software");
BuildLink(gui, "https://factorio.com/");

return Task.CompletedTask;
}

private void BuildLink(ImGui gui, string url, string? text = null) {
Expand Down
5 changes: 4 additions & 1 deletion Yafc/Windows/FilesystemScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using SDL2;
using Yafc.UI;

Expand Down Expand Up @@ -42,7 +43,7 @@ public FilesystemScreen(string? header, string description, string button, strin
previousFocus = InputSystem.Instance.SetDefaultKeyboardFocus(this);
}

protected override void BuildContents(ImGui gui) {
protected override Task BuildContents(ImGui gui) {
gui.BuildText(description, TextBlockDisplayStyle.WrappedText);
if (gui.BuildTextInput(location, out string newLocation, null)) {
if (Directory.Exists(newLocation)) {
Expand All @@ -62,6 +63,8 @@ protected override void BuildContents(ImGui gui) {
}
}
}

return Task.CompletedTask;
}

private void BuildSelectButton(ImGui gui) {
Expand Down
14 changes: 9 additions & 5 deletions Yafc/Windows/MainScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,23 +95,27 @@ private void SetProject(Project project) {
_ = InputSystem.Instance.SetDefaultKeyboardFocus(this);
}

private void ProjectSettingsChanged(bool visualOnly) {
private async void ProjectSettingsChanged(bool visualOnly) {
if (visualOnly) {
return;
}

if (topScreen == null) {
ReRunAnalysis();
await ReRunAnalysis();
}
else {
analysisUpdatePending = true;
}
}

private void ReRunAnalysis() {
private async Task ReRunAnalysis() {
analysisUpdatePending = false;
ErrorCollector collector = new ErrorCollector();
Analysis.ProcessAnalyses(this, project, collector);
project.pages.ForEach(p => p.SetToRecalculate());
Task primary = (activePage?.RunSolveJob() ?? Task.CompletedTask);
await (secondaryPage?.RunSolveJob() ?? Task.CompletedTask);
await primary;
rootGui.MarkEverythingForRebuild();
if (collector.severity > ErrorSeverity.None) {
ErrorListPanel.Show(collector);
Expand Down Expand Up @@ -205,7 +209,7 @@ public void RebuildProjectView() {
secondaryPageView?.bodyContent.MarkEverythingForRebuild();
}

protected override void BuildContent(ImGui gui) {
protected override async Task BuildContent(ImGui gui) {
if (pseudoScreens.Count > 0) {
var top = pseudoScreens[0];
if (gui.isBuilding) {
Expand All @@ -224,7 +228,7 @@ protected override void BuildContent(ImGui gui) {
_ = InputSystem.Instance.SetDefaultKeyboardFocus(this);
topScreen = null;
if (analysisUpdatePending) {
ReRunAnalysis();
await ReRunAnalysis();
}
}
BuildTabBar(gui);
Expand Down
12 changes: 12 additions & 0 deletions Yafc/Windows/PreferencesScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ public override void Build(ImGui gui) {
}
}

using (gui.EnterRowWithHelpIcon($"""
Yafc considers all enabled recipes on your production sheets, even if they are inaccessible. The cost of inaccessible recipes will be multiplied by this amount, to discourage Yafc from using them.
For example, an inaccessible ¥100 recipe will be treated as if it costs ¥{DataUtils.FormatAmount(100 * settings.InaccessibleRecipePenalty, UnitOfMeasure.None)}.
""")) {
gui.BuildText("Cost penalty for inaccessible recipes:", topOffset: 0.5f);
DisplayAmount amount = new(settings.InaccessibleRecipePenalty, UnitOfMeasure.None);
if (gui.BuildFloatInput(amount, TextBoxDisplayStyle.DefaultTextInput with { Prefix = "×" }) && amount.Value >= 1) {
settings.RecordUndo().InaccessibleRecipePenalty = amount.Value;
gui.Rebuild();
}
}

ChooseObject(gui, "Default belt:", Database.allBelts, prefs.defaultBelt, s => {
prefs.RecordUndo().defaultBelt = s;
gui.Rebuild();
Expand Down
5 changes: 4 additions & 1 deletion Yafc/Windows/WelcomeScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using SDL2;
using Serilog;
using Yafc.Model;
Expand Down Expand Up @@ -97,7 +98,7 @@ private void BuildError(ImGui gui) {
gui.DrawRectangle(gui.lastRect, SchemeColor.Error);
}

protected override void BuildContents(ImGui gui) {
protected override Task BuildContents(ImGui gui) {
gui.spacing = 1.5f;
gui.BuildText("Yet Another Factorio Calculator", new TextBlockDisplayStyle(Font.header, Alignment: RectAlignment.Middle));
if (loading) {
Expand Down Expand Up @@ -185,6 +186,8 @@ protected override void BuildContents(ImGui gui) {
}
}
}

return Task.CompletedTask;
}

private void ProjectErrorMoreInfo(ImGui gui) {
Expand Down
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Date:
the launch-settings from the most-recently opened project.
Bugfixes:
- Several fixes to the legacy summary page, including a regression in 0.8.1.
- Milestone-locked and inaccessible recipes will be avoided when multiple recipes are available.
Internal changes:
- Add .git-blame-ignore revs. It doesn't work with Visual Studio, but it might be useful in CLI or other IDEs.
- Add the ability for tests to load lua and run tests that need parts of a Yafc project.
Expand Down