Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
7 changes: 7 additions & 0 deletions FactorioCalc.sln
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

Microsoft Visual Studio Solution File, Format Version 12.00
#
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YAFC", "YAFC\YAFC.csproj", "{73EBA162-A3BE-43CC-9B55-CA16332F439D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YAFCui", "YAFCui\YAFCui.csproj", "{70F74D2B-9747-4185-B369-64F77BC8D0D5}"
Expand All @@ -10,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YAFCparser", "YAFCparser\YA
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandLineToolExample", "CommandLineToolExample\CommandLineToolExample.csproj", "{57E8CAE8-A3F8-4532-B32F-09347852479E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YAFCmodel.Tests", "YAFCmodel.Tests\YAFCmodel.Tests.csproj", "{66B66728-84F0-4242-B49A-B9D746A3CCA5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -36,5 +39,9 @@ Global
{57E8CAE8-A3F8-4532-B32F-09347852479E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{57E8CAE8-A3F8-4532-B32F-09347852479E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{57E8CAE8-A3F8-4532-B32F-09347852479E}.Release|Any CPU.Build.0 = Release|Any CPU
{66B66728-84F0-4242-B49A-B9D746A3CCA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{66B66728-84F0-4242-B49A-B9D746A3CCA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{66B66728-84F0-4242-B49A-B9D746A3CCA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{66B66728-84F0-4242-B49A-B9D746A3CCA5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
4 changes: 1 addition & 3 deletions YAFC/Windows/MilestonesEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@ public override void Build(ImGui gui)
}
if (gui.BuildButton("Add milestone"))
{
if (Project.current.settings.milestones.Count >= 60)
MessageBox.Show(null, "Milestone limit reached", "60 milestones is the limit. You may delete some of the milestones you've already reached.", "Ok");
else SelectObjectPanel.Select(Database.objects.all, "Add new milestone", AddMilestone);
SelectObjectPanel.Select(Database.objects.all, "Add new milestone", AddMilestone);
}
}
}
Expand Down
58 changes: 30 additions & 28 deletions YAFC/Windows/MilestonesPanel.cs
Original file line number Diff line number Diff line change
@@ -1,46 +1,48 @@
using System.Numerics;
using YAFC.Model;
using YAFC.UI;

namespace YAFC
{
public class MilestonesWidget
public class MilestonesWidget : VirtualScrollList<FactorioObject>
{
public static readonly MilestonesWidget Instance = new MilestonesWidget();
public void Build(ImGui gui)

public MilestonesWidget() : base(30f, new Vector2(3f, 3f), MilestoneDrawer)
{
data = Project.current.settings.milestones;
}

private static void MilestoneDrawer(ImGui gui, FactorioObject element, int index)
{
var settings = Project.current.settings;
using (var grid = gui.EnterInlineGrid(3f))
var unlocked = settings.Flags(element).HasFlags(ProjectPerItemFlags.MilestoneUnlocked);
if (gui.BuildFactorioObjectButton(element, 3f, display: MilestoneDisplay.None, bgColor: unlocked ? SchemeColor.Primary : SchemeColor.None))
{
foreach (var cur in settings.milestones)
if (!unlocked)
{
grid.Next();
var unlocked = settings.Flags(cur).HasFlags(ProjectPerItemFlags.MilestoneUnlocked);
if (gui.BuildFactorioObjectButton(cur, 3f, MilestoneDisplay.None, unlocked ? SchemeColor.Primary : SchemeColor.None))
var massUnlock = Milestones.Instance.milestoneResult[element];
var subIndex = 0;
settings.SetFlag(element, ProjectPerItemFlags.MilestoneUnlocked, true);
foreach (var milestone in settings.milestones)
{
if (!unlocked)
{
var massUnlock = Milestones.Instance.milestoneResult[cur];
var subIndex = 0;
settings.SetFlag(cur, ProjectPerItemFlags.MilestoneUnlocked, true);
foreach (var milestone in settings.milestones)
{
subIndex++;
if ((massUnlock & (1ul << subIndex)) != 0)
settings.SetFlag(milestone, ProjectPerItemFlags.MilestoneUnlocked, true);
}
}
else
{
settings.SetFlag(cur, ProjectPerItemFlags.MilestoneUnlocked, false);
}
subIndex++;
if ((massUnlock & (1ul << subIndex)) != 0)
settings.SetFlag(milestone, ProjectPerItemFlags.MilestoneUnlocked, true);
}
if (unlocked && gui.isBuilding)
gui.DrawIcon(gui.lastRect, Icon.Check, SchemeColor.Error);
}
else
{
settings.SetFlag(element, ProjectPerItemFlags.MilestoneUnlocked, false);
}
}
if (unlocked && gui.isBuilding)
gui.DrawIcon(gui.lastRect, Icon.Check, SchemeColor.Error);

}

}

public class MilestonesPanel : PseudoScreen
{
public static readonly MilestonesPanel Instance = new MilestonesPanel();
Expand All @@ -53,14 +55,14 @@ public override void Build(ImGui gui)
gui.AllocateSpacing(2f);
MilestonesWidget.Instance.Build(gui);
gui.AllocateSpacing(2f);
gui.BuildText("For your convinience, YAFC will show objects you DON'T have access to based on this selection", wrap:true);
gui.BuildText("For your convenience, YAFC will show objects you DON'T have access to based on this selection", wrap: true);
gui.BuildText("These are called 'Milestones'. By default all science packs are added as milestones, but this does not have to be this way! " +
"You can define your own milestones: Any item, recipe, entity or technology may be added as a milestone. For example you can add advanced " +
"electronic circuits as a milestone, and YAFC will display everything that is locked behind those circuits", wrap: true);
using (gui.EnterRow())
{
if (gui.BuildButton("Edit milestones", SchemeColor.Grey))
MilestonesEditor.Show();
MilestonesEditor.Show();
if (gui.RemainingRow().BuildButton("Done"))
Close();
}
Expand Down
108 changes: 108 additions & 0 deletions YAFCmodel.Tests/Analysis/Milestones.cs
Comment thread
shpaass marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System.Reflection;
using System.Collections.Generic;
using Xunit;

namespace YAFC.Model.Tests
{
public class MilestonesTests
{
private static Milestones setupMilestones(ulong result, ulong mask, out FactorioObject factorioObj)
{
factorioObj = new Technology();
var milestoneResult = new Mapping<FactorioObject, ulong>(new FactorioIdRange<FactorioObject>(0, 1, new List<FactorioObject>() {
factorioObj
}));
milestoneResult[factorioObj] = result;

var milestones = new Milestones()
{
milestoneResult = milestoneResult,
currentMilestones = new FactorioObject[] { factorioObj }
};

var milestonesType = typeof(Milestones);
var milestonesLockedMask = milestonesType.GetProperty("lockedMask");
milestonesLockedMask.SetValue(milestones, mask);

return milestones;
}

[Theory]
[InlineData(0, 1, false)]
[InlineData(1, 0, false)]
[InlineData(1, 1, true)]
[InlineData(3, 1, true)]
[InlineData(1, 3, true)]
public void IsAccessibleWithCurrentMilestones_WhenGivenMilestones_ShouldReturnCorrectValue(ulong result, ulong mask, bool expectedResult)
{
var milestones = setupMilestones(result, mask, out FactorioObject factorioObj);

Assert.Equal(expectedResult, milestones.IsAccessibleWithCurrentMilestones(0));
Assert.Equal(expectedResult, milestones.IsAccessibleWithCurrentMilestones(factorioObj));
}

[Theory]
[InlineData(1, 1, true)]
[InlineData(3, 3, false)]
[InlineData(15, 15, false)] // Triggers last return
[InlineData(16, 16, false)] // Triggers last return
[InlineData(17, 17, false)] // Caught by 'bit 0 check', otherwise last return would return true
public void IsAccessibleAtNextMilestone_WhenGivenMilestones_ShouldReturnCorrectValue(ulong result, ulong mask, bool expectedResult)
{
var milestones = setupMilestones(result, mask, out FactorioObject factorioObj);

Assert.Equal(expectedResult, milestones.IsAccessibleAtNextMilestone(factorioObj));
}

[Theory]
[InlineData(false, ~0ul)] // all bits set (nothing gets masked)
[InlineData(true, ~0ul & ~2ul)] // all bits set, except bit 1 (for reasons not bit 0, even if the FIRST milestone has its flag set?!)
public void GetLockedMaskFromProject_ShouldCalculateMask(bool unlocked, ulong expectedResult)
{
var milestonesType = typeof(Milestones);
var getLockedMaskFromProject = milestonesType.GetMethod("GetLockedMaskFromProject", BindingFlags.NonPublic | BindingFlags.Instance);
var projectField = milestonesType.GetField("project", BindingFlags.NonPublic | BindingFlags.Instance);

var milestones = setupMilestones(0, 0, out FactorioObject factorioObj);

var project = new Project();
if (unlocked)
{
// Can't use SetFlag() as it uses the Undo system, which requires SDL
var flags = project.settings.itemFlags;
flags[factorioObj] = ProjectPerItemFlags.MilestoneUnlocked;
var projectType = typeof(ProjectSettings);
var itemFlagsField = projectType.GetField("<itemFlags>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance);
itemFlagsField.SetValue(project.settings, flags);
}


projectField.SetValue(milestones, project);

getLockedMaskFromProject.Invoke(milestones, null);

Assert.Equal(expectedResult, milestones.lockedMask);
}

[Theory]
[InlineData(1, 0, true, false)] // HighestBitSet() - 1, so bit 0 is never in range...
[InlineData(2, 0, true, true)] // mask is ignored -> true
[InlineData(2, 0, false, false)] // mask is active -> false
[InlineData(2, 2, true, true)]
[InlineData(2, 2, false, true)] // mask is active and overlaps -> true
[InlineData(4, 0, true, false)] // HighestBitSet() too large...
public void GetHighest_WhenGivenMilestones_ShouldReturnCorrectValue(ulong result, ulong mask, bool all, bool expectObject)
{
var milestones = setupMilestones(result, mask, out FactorioObject factorioObj);

if (expectObject)
{
Assert.Equal(factorioObj, milestones.GetHighest(factorioObj, all));
}
else
{
Assert.Null(milestones.GetHighest(factorioObj, all));
}
}
}
}
21 changes: 21 additions & 0 deletions YAFCmodel.Tests/YAFCmodel.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp6.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\YAFCmodel\YAFCmodel.csproj" />
<ProjectReference Include="..\YAFCparser\YAFCparser.csproj" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions YAFCmodel/Analysis/Milestones.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public bool IsAccessibleAtNextMilestone(FactorioObject obj)
return true;
if ((milestoneMask & 1) != 0)
return false;
// TODO Always returns false -> milestoneMask is a power of 2 + 1 always has bit 0 set, as x pow 2 sets one (high) bit, so the + 1 adds bit 0, which is detected by (milestoneMask & 1) != 0
return ((milestoneMask - 1) & (milestoneMask - 2)) == 0; // milestoneMask is a power of 2 + 1
}

Expand Down
4 changes: 2 additions & 2 deletions YAFCmodel/Data/Database.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public class FactorioIdRange<T> where T : FactorioObject
public int count { get; }
public T[] all { get; }

internal FactorioIdRange(int start, int end, List<FactorioObject> source)
public FactorioIdRange(int start, int end, List<FactorioObject> source)
{
this.start = start;
count = end-start;
Expand Down Expand Up @@ -110,7 +110,7 @@ public Mapping<T, TValue> CreateMapping<TValue>(Func<T, TValue> mapFunc)
private readonly int offset;
private readonly TValue[] data;
private readonly FactorioIdRange<TKey> source;
internal Mapping(FactorioIdRange<TKey> source)
public Mapping(FactorioIdRange<TKey> source)
{
this.source = source;
data = new TValue[source.count];
Expand Down