From 05c5c82463bc5f800a57a5a3987795568c2cf0cf Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 9 Aug 2025 16:51:27 +0000 Subject: [PATCH 1/5] This commit introduces a new test harness for the `Micropolis.Core` project. The test harness is designed to help diagnose a bug where city metrics trend to zero. The test harness consists of a new xUnit test project that: 1. Initializes the Micropolis engine. 2. Runs the simulation for 100 ticks. 3. Captures the state of the engine at each tick, including the full map state and the simulation phase cycle. 4. Serializes the history of captured states to a JSON file named `engine_history.json`. This will allow you to analyze the state of the engine over time and identify the cause of the bug. NOTE: I was unable to compile and run the tests to verify the changes. The code has been written based on my knowledge of the C# language and .NET ecosystem, following best practices. --- Micropolis.Core.Test/EngineState.cs | 26 +++++++++ Micropolis.Core.Test/EngineTests.cs | 56 +++++++++++++++++++ .../Micropolis.Core.Test.csproj | 27 +++++++++ MicropolisSharp.sln | 10 ++++ 4 files changed, 119 insertions(+) create mode 100644 Micropolis.Core.Test/EngineState.cs create mode 100644 Micropolis.Core.Test/EngineTests.cs create mode 100644 Micropolis.Core.Test/Micropolis.Core.Test.csproj diff --git a/Micropolis.Core.Test/EngineState.cs b/Micropolis.Core.Test/EngineState.cs new file mode 100644 index 0000000..9ac5ae7 --- /dev/null +++ b/Micropolis.Core.Test/EngineState.cs @@ -0,0 +1,26 @@ +namespace Micropolis.Core.Test +{ + public class EngineState + { + public long CityTime { get; set; } + public int TotalPop { get; set; } + public long TotalFunds { get; set; } + public int ResPop { get; set; } + public int ComPop { get; set; } + public int IndPop { get; set; } + public short ResValve { get; set; } + public short ComValve { get; set; } + public short IndValve { get; set; } + public int CrimeAverage { get; set; } + public int PollutionAverage { get; set; } + public int LandValueAverage { get; set; } + public long RoadFund { get; set; } + public long PoliceFund { get; set; } + public long FireFund { get; set; } + public long RoadSpend { get; set; } + public long PoliceSpend { get; set; } + public long FireSpend { get; set; } + public short PhaseCycle { get; set; } + public ushort[,] Map { get; set; } + } +} diff --git a/Micropolis.Core.Test/EngineTests.cs b/Micropolis.Core.Test/EngineTests.cs new file mode 100644 index 0000000..0b6fa05 --- /dev/null +++ b/Micropolis.Core.Test/EngineTests.cs @@ -0,0 +1,56 @@ +using MicropolisSharp; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using Xunit; + +namespace Micropolis.Core.Test +{ + public class EngineTests + { + [Fact] + public void RunSimulationAndCaptureState() + { + Micropolis engine = new Micropolis(); + engine.SimInit(); + engine.GenerateMap(); + + List history = new List(); + + for (int i = 0; i < 100; i++) + { + engine.SimTick(); + + var state = new EngineState + { + CityTime = engine.CityTime, + TotalPop = engine.TotalPop, + TotalFunds = engine.TotalFunds, + ResPop = engine.ResPop, + ComPop = engine.ComPop, + IndPop = engine.IndPop, + ResValve = engine.ResValve, + ComValve = engine.ComValve, + IndValve = engine.IndValve, + CrimeAverage = engine.CrimeAverage, + PollutionAverage = engine.PollutionAverage, + LandValueAverage = engine.LandValueAverage, + RoadFund = engine.RoadFund, + PoliceFund = engine.PoliceFund, + FireFund = engine.FireFund, + RoadSpend = engine.RoadSpend, + PoliceSpend = engine.PoliceSpend, + FireSpend = engine.FireSpend, + PhaseCycle = engine.PhaseCycle, + Map = (ushort[,])engine.Map.Clone() + }; + + history.Add(state); + } + + var options = new JsonSerializerOptions { WriteIndented = true }; + string jsonString = JsonSerializer.Serialize(history, options); + File.WriteAllText("engine_history.json", jsonString); + } + } +} diff --git a/Micropolis.Core.Test/Micropolis.Core.Test.csproj b/Micropolis.Core.Test/Micropolis.Core.Test.csproj new file mode 100644 index 0000000..05cbb77 --- /dev/null +++ b/Micropolis.Core.Test/Micropolis.Core.Test.csproj @@ -0,0 +1,27 @@ + + + + net6.0 + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/MicropolisSharp.sln b/MicropolisSharp.sln index b2dd3ab..327ac7d 100644 --- a/MicropolisSharp.sln +++ b/MicropolisSharp.sln @@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Micropolis.Core", "Micropol EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Micropolis.Windows", "Micropolis.Windows\Micropolis.Windows.csproj", "{A4F88EA5-F359-404A-A49A-20348A73451B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Micropolis.Core.Test", "Micropolis.Core.Test\Micropolis.Core.Test.csproj", "{86A2AC73-2334-400A-A626-75B7F3469A4B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -31,6 +33,14 @@ Global {A4F88EA5-F359-404A-A49A-20348A73451B}.Release|Any CPU.Build.0 = Release|Any CPU {A4F88EA5-F359-404A-A49A-20348A73451B}.Release|x86.ActiveCfg = Release|Any CPU {A4F88EA5-F359-404A-A49A-20348A73451B}.Release|x86.Build.0 = Release|Any CPU + {86A2AC73-2334-400A-A626-75B7F3469A4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86A2AC73-2334-400A-A626-75B7F3469A4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86A2AC73-2334-400A-A626-75B7F3469A4B}.Debug|x86.ActiveCfg = Debug|Any CPU + {86A2AC73-2334-400A-A626-75B7F3469A4B}.Debug|x86.Build.0 = Debug|Any CPU + {86A2AC73-2334-400A-A626-75B7F3469A4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86A2AC73-2334-400A-A626-75B7F3469A4B}.Release|Any CPU.Build.0 = Release|Any CPU + {86A2AC73-2334-400A-A626-75B7F3469A4B}.Release|x86.ActiveCfg = Release|Any CPU + {86A2AC73-2334-400A-A626-75B7F3469A4B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 6fd86bbcff816ebd06f07ad768d4e27d69f2eb45 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 9 Aug 2025 17:01:54 +0000 Subject: [PATCH 2/5] I've created a new test harness for the `Micropolis.Core` project. I designed it to help you diagnose a bug where city metrics trend to zero. The test harness consists of a new xUnit test project that: 1. Initializes the Micropolis engine. 2. Runs the simulation for 100 ticks. 3. Captures the state of the engine at each tick, including the full map state, the simulation phase cycle, and the power grid map. 4. Serializes the history of captured states to a JSON file named `engine_history.json`. This will allow you to analyze the state of the engine over time and identify the cause of the bug. NOTE: Due to limitations in my execution environment, I was unable to compile and run the tests to verify the changes. I have written the code based on best practices and my knowledge of the C# language and .NET ecosystem. --- Micropolis.Core.Test/EngineState.cs | 1 + Micropolis.Core.Test/EngineTests.cs | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Micropolis.Core.Test/EngineState.cs b/Micropolis.Core.Test/EngineState.cs index 9ac5ae7..7583504 100644 --- a/Micropolis.Core.Test/EngineState.cs +++ b/Micropolis.Core.Test/EngineState.cs @@ -22,5 +22,6 @@ public class EngineState public long FireSpend { get; set; } public short PhaseCycle { get; set; } public ushort[,] Map { get; set; } + public byte[,] PowerGridMap { get; set; } } } diff --git a/Micropolis.Core.Test/EngineTests.cs b/Micropolis.Core.Test/EngineTests.cs index 0b6fa05..f1e4695 100644 --- a/Micropolis.Core.Test/EngineTests.cs +++ b/Micropolis.Core.Test/EngineTests.cs @@ -42,7 +42,8 @@ public void RunSimulationAndCaptureState() PoliceSpend = engine.PoliceSpend, FireSpend = engine.FireSpend, PhaseCycle = engine.PhaseCycle, - Map = (ushort[,])engine.Map.Clone() + Map = (ushort[,])engine.Map.Clone(), + PowerGridMap = ConvertPowerGridMap(engine.PowerGridMap) }; history.Add(state); @@ -52,5 +53,23 @@ public void RunSimulationAndCaptureState() string jsonString = JsonSerializer.Serialize(history, options); File.WriteAllText("engine_history.json", jsonString); } + + private byte[,] ConvertPowerGridMap(MicropolisSharp.Types.ByteMap1 powerGridMap) + { + var width = powerGridMap.width; + var height = powerGridMap.height; + var data1D = powerGridMap.getBase(); + var data2D = new byte[width, height]; + + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + data2D[x, y] = data1D[x * height + y]; + } + } + + return data2D; + } } } From b65bd59333e2f8a776bb11f8b2508faea159f112 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 9 Aug 2025 17:22:02 +0000 Subject: [PATCH 3/5] This commit introduces a new test harness for the `Micropolis.Core` project. The test harness is designed to help diagnose a bug where city metrics trend to zero. The test harness consists of a new xUnit test project that: 1. Initializes the Micropolis engine. 2. Runs the simulation for 100 ticks. 3. Captures the state of the engine at each tick, including: - Full map state - Simulation phase cycle - Power grid map - An ASCII representation of the power grid map 4. Serializes the history of captured states to a JSON file named `engine_history.json`. This will allow you to analyze the state of the engine over time and identify the cause of the bug. NOTE: I was unable to compile and run the tests to verify the changes. The code has been written based on best practices and knowledge of the C# language and .NET ecosystem. --- Micropolis.Core.Test/EngineState.cs | 1 + Micropolis.Core.Test/EngineTests.cs | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Micropolis.Core.Test/EngineState.cs b/Micropolis.Core.Test/EngineState.cs index 7583504..bcec615 100644 --- a/Micropolis.Core.Test/EngineState.cs +++ b/Micropolis.Core.Test/EngineState.cs @@ -23,5 +23,6 @@ public class EngineState public short PhaseCycle { get; set; } public ushort[,] Map { get; set; } public byte[,] PowerGridMap { get; set; } + public string AsciiPowerMap { get; set; } } } diff --git a/Micropolis.Core.Test/EngineTests.cs b/Micropolis.Core.Test/EngineTests.cs index f1e4695..d3ce956 100644 --- a/Micropolis.Core.Test/EngineTests.cs +++ b/Micropolis.Core.Test/EngineTests.cs @@ -43,7 +43,8 @@ public void RunSimulationAndCaptureState() FireSpend = engine.FireSpend, PhaseCycle = engine.PhaseCycle, Map = (ushort[,])engine.Map.Clone(), - PowerGridMap = ConvertPowerGridMap(engine.PowerGridMap) + PowerGridMap = ConvertPowerGridMap(engine.PowerGridMap), + AsciiPowerMap = GenerateAsciiPowerMap(engine.PowerGridMap) }; history.Add(state); @@ -71,5 +72,25 @@ public void RunSimulationAndCaptureState() return data2D; } + + private string GenerateAsciiPowerMap(MicropolisSharp.Types.ByteMap1 powerGridMap) + { + var width = powerGridMap.width; + var height = powerGridMap.height; + var data = powerGridMap.getBase(); + var sb = new System.Text.StringBuilder(); + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + var value = data[x * height + y]; + sb.Append(value > 0 ? '#' : ' '); + } + sb.AppendLine(); + } + + return sb.ToString(); + } } } From ed1da459a2009276340c25e25ce4e4c45b89236c Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 10 Aug 2025 13:33:25 +0000 Subject: [PATCH 4/5] This commit introduces a new test harness for the `Micropolis.Core` project. The test harness is designed to help diagnose a bug where city metrics trend to zero. The test harness consists of a new xUnit test project that: 1. Loads the "kobe.cty" city file. 2. Runs the simulation for 100 ticks. 3. Captures the state of the engine at each tick, including: - Full map state - Simulation phase cycle - Power grid map - An ASCII representation of the power grid map 4. Serializes the history of captured states to a JSON file named `engine_history.json`. This will allow you to analyze the state of the engine over time and identify the cause of the bug. NOTE: I was unable to compile and run the tests to verify the changes. However, I wrote the code based on best practices and my knowledge of the C# language and .NET ecosystem. --- Micropolis.Core.Test/EngineTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Micropolis.Core.Test/EngineTests.cs b/Micropolis.Core.Test/EngineTests.cs index d3ce956..d63e24f 100644 --- a/Micropolis.Core.Test/EngineTests.cs +++ b/Micropolis.Core.Test/EngineTests.cs @@ -13,7 +13,10 @@ public void RunSimulationAndCaptureState() { Micropolis engine = new Micropolis(); engine.SimInit(); - engine.GenerateMap(); + + string cityPath = "../../../Micropolis.Windows/cities/kobe.cty"; + bool success = engine.LoadCity(cityPath); + Assert.True(success, "Failed to load city file."); List history = new List(); From 5ed95854b63a6abf5ea3acccc769c539944a32ff Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 10 Aug 2025 13:45:39 +0000 Subject: [PATCH 5/5] This commit fixes a `JsonException` that occurred when serializing the engine state. The default `System.Text.Json` serializer does not support two-dimensional arrays, which caused an error when trying to serialize the `Map` and `PowerGridMap` properties. The fix involves converting the `ushort[,]` and `byte[,]` arrays to jagged arrays (`ushort[][]` and `byte[][]`) before serialization. This is done by adding helper methods to the `EngineTests` class. This change allows the test harness to correctly serialize the full engine state, including map data, to the `engine_history.json` file. --- Micropolis.Core.Test/EngineState.cs | 4 ++-- Micropolis.Core.Test/EngineTests.cs | 35 ++++++++++++++++++++++------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/Micropolis.Core.Test/EngineState.cs b/Micropolis.Core.Test/EngineState.cs index bcec615..198caf1 100644 --- a/Micropolis.Core.Test/EngineState.cs +++ b/Micropolis.Core.Test/EngineState.cs @@ -21,8 +21,8 @@ public class EngineState public long PoliceSpend { get; set; } public long FireSpend { get; set; } public short PhaseCycle { get; set; } - public ushort[,] Map { get; set; } - public byte[,] PowerGridMap { get; set; } + public ushort[][] Map { get; set; } + public byte[][] PowerGridMap { get; set; } public string AsciiPowerMap { get; set; } } } diff --git a/Micropolis.Core.Test/EngineTests.cs b/Micropolis.Core.Test/EngineTests.cs index d63e24f..bd835f0 100644 --- a/Micropolis.Core.Test/EngineTests.cs +++ b/Micropolis.Core.Test/EngineTests.cs @@ -45,8 +45,8 @@ public void RunSimulationAndCaptureState() PoliceSpend = engine.PoliceSpend, FireSpend = engine.FireSpend, PhaseCycle = engine.PhaseCycle, - Map = (ushort[,])engine.Map.Clone(), - PowerGridMap = ConvertPowerGridMap(engine.PowerGridMap), + Map = ConvertMapToJagged(engine.Map), + PowerGridMap = ConvertPowerGridMapToJagged(engine.PowerGridMap), AsciiPowerMap = GenerateAsciiPowerMap(engine.PowerGridMap) }; @@ -58,22 +58,41 @@ public void RunSimulationAndCaptureState() File.WriteAllText("engine_history.json", jsonString); } - private byte[,] ConvertPowerGridMap(MicropolisSharp.Types.ByteMap1 powerGridMap) + private ushort[][] ConvertMapToJagged(ushort[,] map) + { + int width = map.GetLength(0); + int height = map.GetLength(1); + ushort[][] jaggedMap = new ushort[height][]; + + for (int y = 0; y < height; y++) + { + jaggedMap[y] = new ushort[width]; + for (int x = 0; x < width; x++) + { + jaggedMap[y][x] = map[x, y]; + } + } + + return jaggedMap; + } + + private byte[][] ConvertPowerGridMapToJagged(MicropolisSharp.Types.ByteMap1 powerGridMap) { var width = powerGridMap.width; var height = powerGridMap.height; var data1D = powerGridMap.getBase(); - var data2D = new byte[width, height]; + var jaggedMap = new byte[height][]; - for (int x = 0; x < width; x++) + for (int y = 0; y < height; y++) { - for (int y = 0; y < height; y++) + jaggedMap[y] = new byte[width]; + for (int x = 0; x < width; x++) { - data2D[x, y] = data1D[x * height + y]; + jaggedMap[y][x] = data1D[x * height + y]; } } - return data2D; + return jaggedMap; } private string GenerateAsciiPowerMap(MicropolisSharp.Types.ByteMap1 powerGridMap)