Skip to content

Commit 2c71ea8

Browse files
committed
dsstats build base
1 parent cdf7030 commit 2c71ea8

File tree

13 files changed

+405
-0
lines changed

13 files changed

+405
-0
lines changed
4.92 MB
Loading
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31903.59
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dsstats.builder", "dsstats.builder\dsstats.builder.csproj", "{887D37FF-C0EA-45A4-BAC2-F64297FC072F}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dsstats.builder.tests", "dsstats.builder.tests\dsstats.builder.tests.csproj", "{63A5A629-7A07-44BE-9202-8022C30C9983}"
9+
EndProject
10+
Global
11+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
12+
Debug|Any CPU = Debug|Any CPU
13+
Debug|x64 = Debug|x64
14+
Debug|x86 = Debug|x86
15+
Release|Any CPU = Release|Any CPU
16+
Release|x64 = Release|x64
17+
Release|x86 = Release|x86
18+
EndGlobalSection
19+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
20+
{887D37FF-C0EA-45A4-BAC2-F64297FC072F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{887D37FF-C0EA-45A4-BAC2-F64297FC072F}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{887D37FF-C0EA-45A4-BAC2-F64297FC072F}.Debug|x64.ActiveCfg = Debug|Any CPU
23+
{887D37FF-C0EA-45A4-BAC2-F64297FC072F}.Debug|x64.Build.0 = Debug|Any CPU
24+
{887D37FF-C0EA-45A4-BAC2-F64297FC072F}.Debug|x86.ActiveCfg = Debug|Any CPU
25+
{887D37FF-C0EA-45A4-BAC2-F64297FC072F}.Debug|x86.Build.0 = Debug|Any CPU
26+
{887D37FF-C0EA-45A4-BAC2-F64297FC072F}.Release|Any CPU.ActiveCfg = Release|Any CPU
27+
{887D37FF-C0EA-45A4-BAC2-F64297FC072F}.Release|Any CPU.Build.0 = Release|Any CPU
28+
{887D37FF-C0EA-45A4-BAC2-F64297FC072F}.Release|x64.ActiveCfg = Release|Any CPU
29+
{887D37FF-C0EA-45A4-BAC2-F64297FC072F}.Release|x64.Build.0 = Release|Any CPU
30+
{887D37FF-C0EA-45A4-BAC2-F64297FC072F}.Release|x86.ActiveCfg = Release|Any CPU
31+
{887D37FF-C0EA-45A4-BAC2-F64297FC072F}.Release|x86.Build.0 = Release|Any CPU
32+
{63A5A629-7A07-44BE-9202-8022C30C9983}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{63A5A629-7A07-44BE-9202-8022C30C9983}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{63A5A629-7A07-44BE-9202-8022C30C9983}.Debug|x64.ActiveCfg = Debug|Any CPU
35+
{63A5A629-7A07-44BE-9202-8022C30C9983}.Debug|x64.Build.0 = Debug|Any CPU
36+
{63A5A629-7A07-44BE-9202-8022C30C9983}.Debug|x86.ActiveCfg = Debug|Any CPU
37+
{63A5A629-7A07-44BE-9202-8022C30C9983}.Debug|x86.Build.0 = Debug|Any CPU
38+
{63A5A629-7A07-44BE-9202-8022C30C9983}.Release|Any CPU.ActiveCfg = Release|Any CPU
39+
{63A5A629-7A07-44BE-9202-8022C30C9983}.Release|Any CPU.Build.0 = Release|Any CPU
40+
{63A5A629-7A07-44BE-9202-8022C30C9983}.Release|x64.ActiveCfg = Release|Any CPU
41+
{63A5A629-7A07-44BE-9202-8022C30C9983}.Release|x64.Build.0 = Release|Any CPU
42+
{63A5A629-7A07-44BE-9202-8022C30C9983}.Release|x86.ActiveCfg = Release|Any CPU
43+
{63A5A629-7A07-44BE-9202-8022C30C9983}.Release|x86.Build.0 = Release|Any CPU
44+
EndGlobalSection
45+
GlobalSection(SolutionProperties) = preSolution
46+
HideSolutionNode = FALSE
47+
EndGlobalSection
48+
EndGlobal
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace dsstats.builder.tests;
2+
3+
[TestClass]
4+
public sealed class BuildAreaTests
5+
{
6+
[TestMethod]
7+
public void CanPlaceUnit()
8+
{
9+
var buildArea = new BuildArea();
10+
var result = buildArea.PlaceUnit("Zergling", new(90, 75));
11+
Assert.IsTrue(result);
12+
}
13+
14+
[TestMethod]
15+
public void CannotPlaceUnit()
16+
{
17+
var buildArea = new BuildArea();
18+
var result = buildArea.PlaceUnit("Zergling", new(0, 0));
19+
Assert.IsFalse(result);
20+
}
21+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace dsstats.builder.tests;
2+
3+
[TestClass]
4+
public sealed class MapTests
5+
{
6+
7+
8+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
namespace dsstats.builder.tests;
2+
3+
[TestClass]
4+
public sealed class ScreenAreaTests
5+
{
6+
[TestMethod]
7+
public void CanCalculateCenter()
8+
{
9+
var screenArea = new ScreenArea(2560, 1440);
10+
var center = screenArea.GetCenter();
11+
var expected = new RlPoint(1280, 600);
12+
Assert.AreEqual(expected, center, $"Wrong center: ({center.X},{center.Y}");
13+
}
14+
15+
[TestMethod]
16+
public void RespectesScreenResolution()
17+
{
18+
var screenArea1 = new ScreenArea(2560, 1440);
19+
var screenArea2 = new ScreenArea(1920, 1080);
20+
var center1 = screenArea1.GetCenter();
21+
var center2 = screenArea2.GetCenter();
22+
23+
Assert.AreNotEqual(center1, center2);
24+
}
25+
26+
[TestMethod]
27+
public void CanMapCenterPoint()
28+
{
29+
var screenArea = new ScreenArea(2560, 1440);
30+
var buildArea = new BuildArea();
31+
var buildCenter = buildArea.GetCenter();
32+
var screenPos = screenArea.GetScreenPosition(buildCenter);
33+
var expected = screenArea.GetCenter();
34+
Assert.AreEqual(expected, screenPos, $"point map failed: ({expected.X},{expected.Y}) => ({screenPos.X},{screenPos.Y})");
35+
}
36+
37+
[TestMethod]
38+
public void CanMapLeftPoint()
39+
{
40+
var screenArea = new ScreenArea(2560, 1440);
41+
var replayPos = new RlPoint(73, 82);
42+
var screenPos = screenArea.GetScreenPosition(replayPos);
43+
44+
var expected = new RlPoint(485, 437);
45+
Assert.AreEqual(expected, screenPos, $"point map failed: ({expected.X},{expected.Y}) => ({screenPos.X},{screenPos.Y})");
46+
}
47+
48+
[TestMethod]
49+
public void CanMapRightPoint()
50+
{
51+
var screenArea = new ScreenArea(2560, 1440);
52+
var replayPos = new RlPoint(101, 76);
53+
var screenPos = screenArea.GetScreenPosition(replayPos);
54+
55+
var expected = new RlPoint(2100, 765);
56+
Assert.AreEqual(expected, screenPos, $"point map failed: ({expected.X},{expected.Y}) => ({screenPos.X},{screenPos.Y})");
57+
}
58+
59+
[TestMethod]
60+
public void CanMapTopPoint()
61+
{
62+
var screenArea = new ScreenArea(2560, 1440);
63+
var replayPos = new RlPoint(84, 93);
64+
var screenPos = screenArea.GetScreenPosition(replayPos);
65+
66+
var expected = new RlPoint(1124, -110);
67+
Assert.AreEqual(expected, screenPos, $"point map failed: ({expected.X},{expected.Y}) => ({screenPos.X},{screenPos.Y})");
68+
}
69+
70+
[TestMethod]
71+
public void CanMapBottomPoint()
72+
{
73+
var screenArea = new ScreenArea(2560, 1440);
74+
var replayPos = new RlPoint(90, 65);
75+
var screenPos = screenArea.GetScreenPosition(replayPos);
76+
77+
var expected = new RlPoint(1468, 1423);
78+
Assert.AreEqual(expected, screenPos, $"point map failed: ({expected.X},{expected.Y}) => ({screenPos.X},{screenPos.Y})");
79+
}
80+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<LangVersion>latest</LangVersion>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
12+
<PackageReference Include="MSTest" Version="3.6.4" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<ProjectReference Include="..\dsstats.builder\dsstats.builder.csproj" />
21+
</ItemGroup>
22+
23+
</Project>
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
namespace dsstats.builder;
2+
3+
/// <summary>
4+
/// describes a 45° rotated rectangle of squares as used in the Starcraft II map editor of the Direct Strike map
5+
/// sc2 replays contain the unit coordinates based on it
6+
/// each interger coordinate inside the polygan can hold one ground and one air unit
7+
/// the unrotated rectangle does have 12 rows with 18 columns and alternately 11 rows with 17 columns as a grid of diamonds
8+
/// </summary>
9+
public class BuildArea
10+
{
11+
private List<RlPoint> polygon =
12+
[
13+
new RlPoint(73, 82), // Left
14+
new RlPoint(84, 93), // Top
15+
new RlPoint(101, 76), // Right
16+
new RlPoint(90, 65), // Bottom
17+
];
18+
private Dictionary<string, HashSet<RlPoint>> units = [];
19+
20+
public bool PlaceUnit(string unit, RlPoint position)
21+
{
22+
if (!IsPointInsideOrOnEdge(position))
23+
{
24+
return false;
25+
}
26+
if (!units.TryGetValue(unit, out var unitPositions) || unitPositions == null)
27+
{
28+
unitPositions = units[unit] = [];
29+
}
30+
unitPositions.Add(position);
31+
return true;
32+
}
33+
34+
public RlPoint GetCenter()
35+
{
36+
int x1 = polygon.Min(m => m.X);
37+
int y1 = polygon.Min(m => m.Y);
38+
int x2 = polygon.Max(m => m.X);
39+
int y2 = polygon.Max(m => m.Y);
40+
41+
return new(x1 + ((x2 - x1) / 2), y1 + ((y2 - y1) / 2));
42+
}
43+
44+
private bool IsPointInsideOrOnEdge(RlPoint p)
45+
{
46+
if (IsPointInPolygon(p, polygon))
47+
return true;
48+
49+
for (int i = 0; i < polygon.Count; i++)
50+
{
51+
if (IsOnEdge(p, polygon[i], polygon[(i + 1) % polygon.Count]))
52+
return true;
53+
}
54+
55+
return false;
56+
}
57+
58+
private static bool IsPointInPolygon(RlPoint p, List<RlPoint> polygon)
59+
{
60+
int wn = 0; // winding number
61+
int n = polygon.Count;
62+
63+
for (int i = 0; i < n; i++)
64+
{
65+
RlPoint pi = polygon[i];
66+
RlPoint pj = polygon[(i + 1) % n];
67+
68+
if (pi.Y <= p.Y)
69+
{
70+
if (pj.Y > p.Y && IsLeft(pi, pj, p) > 0)
71+
wn++;
72+
}
73+
else
74+
{
75+
if (pj.Y <= p.Y && IsLeft(pi, pj, p) < 0)
76+
wn--;
77+
}
78+
}
79+
80+
return wn != 0;
81+
}
82+
83+
private static double IsLeft(RlPoint p0, RlPoint p1, RlPoint p2)
84+
{
85+
return (p1.X - p0.X) * (p2.Y - p0.Y) - (p2.X - p0.X) * (p1.Y - p0.Y);
86+
}
87+
88+
private static bool IsOnEdge(RlPoint p, RlPoint a, RlPoint b)
89+
{
90+
double cross = (p.Y - a.Y) * (b.X - a.X) - (p.X - a.X) * (b.Y - a.Y);
91+
if (Math.Abs(cross) > 1e-6) return false;
92+
93+
double dot = (p.X - a.X) * (b.X - a.X) + (p.Y - a.Y) * (b.Y - a.Y);
94+
if (dot < 0) return false;
95+
96+
double lenSq = (b.X - a.X) * (b.X - a.X) + (b.Y - a.Y) * (b.Y - a.Y);
97+
return dot <= lenSq;
98+
}
99+
}
100+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace dsstats.builder;
2+
3+
class Program
4+
{
5+
static void Main(string[] args)
6+
{
7+
Console.WriteLine("Hello, World!");
8+
}
9+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace dsstats.builder;
2+
3+
public sealed record RlPoint(int X, int Y)
4+
{
5+
public static RlPoint Lerp(RlPoint a, RlPoint b, double t)
6+
{
7+
return new RlPoint(
8+
(int)(a.X + (b.X - a.X) * t),
9+
(int)(a.Y + (b.Y - a.Y) * t)
10+
);
11+
}
12+
13+
public static RlPoint operator +(RlPoint a, RlPoint b) => new(a.X + b.X, a.Y + b.Y);
14+
public static RlPoint operator -(RlPoint a, RlPoint b) => new(a.X - b.X, a.Y - b.Y);
15+
public static RlPoint operator /(RlPoint a, int scalar) => new(a.X / scalar, a.Y / scalar);
16+
17+
public static RlPoint Zero => new(0, 0);
18+
}

0 commit comments

Comments
 (0)