1
0

add visualization and report

This commit is contained in:
Rokas Puzonas 2023-05-30 00:41:29 +03:00
parent 661b55ace6
commit 3a4a5f6021
9 changed files with 1069 additions and 182 deletions

282
Lab4/Algorithm.cs Normal file
View File

@ -0,0 +1,282 @@
using System.Diagnostics;
namespace Lab4
{
public class Algorithm
{
/*
Faile places_data.xlsx pateikta informacija apie lankytinas vietas (1 lentelė). Tikslas: kaip galima pigesnio maršruto sudarymas kai,
* priimama, kad kelionės tarp vietų kaina lygi kvadratinei šakniai kelionės atstumo;
* kelionės pradžios ir pabaigos vieta sutampa (su grįžimu atgal);
* ta pati vieta negali būti aplankyta daugiau nei vieną;
* Reikia aplankyti bet kurias 150 vietų pateikto sąrašo.
*/
public int pathLength = 150;
public int batchSize = 40;
public float mutationPickUniqueChance = 0.005f;
public float mutationSwapChance = 0.01f;
public float mutationPickLocalChance = 0.005f;
public float localAreaRadius = 70000.000f;
public int seed;
public int iteration = 0;
public SortedList<double, List<int>> paths;
public List<List<int>> localPlaces;
List<Place> places;
Random rand;
public Algorithm(List<Place> places, int seed)
{
this.places = places;
this.seed = seed;
rand = new Random(seed);
paths = GenerateInitialPaths(rand, places, pathLength, batchSize);
localPlaces = ComputeLocalNodes(places, localAreaRadius);
}
public Algorithm(List<Place> places) : this(places, (int)DateTime.Now.Ticks) { }
public void Reset()
{
rand = new Random(seed);
paths = GenerateInitialPaths(rand, places, pathLength, batchSize);
localPlaces = ComputeLocalNodes(places, localAreaRadius);
iteration = 0;
}
public class Place
{
public string name;
public float x;
public float y;
public Place(string name, float x, float y)
{
this.name = name;
this.x = x;
this.y = y;
}
}
public static double GetDistance(float x0, float y0, float x1, float y1)
{
float dx = x0 - x1;
float dy = y0 - y1;
return Math.Sqrt(dx * dx + dy * dy);
}
public static double GetDistance(Place A, Place B)
{
return GetDistance(A.x, A.y, B.x, B.y);
}
public static double GetTravalCost(Place A, Place B)
{
return Math.Sqrt(GetDistance(A, B));
}
public static List<Place> ReadPlacesFromTSV(string filename)
{
List<Place> cities = new List<Place>();
foreach (var line in File.ReadLines(filename))
{
string[] parts = line.Split("\t");
string name = parts[0];
float x = float.Parse(parts[2]);
float y = float.Parse(parts[3]);
cities.Add(new Place(name, x, y));
}
return cities;
}
// Warning 'PickUnqiueNode' could get stuck in an infinite loop, if there are not enough places
public static int PickUnqiueNode(Random rand, List<int> path, int placesCount)
{
while (true)
{
int num = rand.Next(0, placesCount);
if (!path.Contains(num))
{
return num;
}
}
}
public static List<List<int>> ComputeLocalNodes(List<Place> places, float localRadius)
{
var locals = new List<List<int>>(places.Count);
for (int i = 0; i < places.Count; i++)
{
var nodeLocals = new List<int>();
for (int j = 0; j < places.Count; j++)
{
if (i == j) continue;
if (GetDistance(places[i], places[j]) < localRadius)
{
nodeLocals.Add(j);
}
}
locals.Add(nodeLocals);
}
return locals;
}
public static int PickUniqueLocalNode(Random rand, List<int> path, List<int> localPlaces)
{
if (localPlaces.Count == 0) return -1;
int retryCount = 5;
for (int i = 0; i < retryCount; i++)
{
int idx = rand.Next(0, localPlaces.Count);
int place = localPlaces[idx];
if (!path.Contains(place))
{
return place;
}
}
return -1;
}
public static List<int> GenerateRandomPath(Random rand, int size, int placesCount)
{
List<int> path = new List<int>(size);
for (int i = 0; i < size; i++)
{
path.Add(PickUnqiueNode(rand, path, placesCount));
}
return path;
}
public static double GetPathCost(List<int> path, List<Place> places)
{
double cost = 0;
for (int i = 0; i < path.Count - 1; i++)
{
int from = path[i];
int to = path[i + 1];
cost += GetTravalCost(places[from], places[to]);
}
cost += GetTravalCost(places[path.First()], places[path.Last()]);
return cost;
}
public static List<int> SplicePaths(Random rand, List<int> A, List<int> B)
{
Debug.Assert(A.Count == B.Count);
List<int> spliced = new List<int>(A.Count);
for (int i = 0; i < A.Count - 1; i++)
{
spliced[i] = rand.Next(0, 2) == 0 ? A[i] : B[i];
}
return spliced;
}
public static SortedList<double, List<int>> GenerateInitialPaths(Random rand, List<Place> places, int pathLength, int pathCount)
{
var paths = new SortedList<double, List<int>>();
for (int i = 0; i < pathCount; i++)
{
var path = GenerateRandomPath(rand, pathLength, places.Count);
var cost = GetPathCost(path, places);
paths.Add(cost, path);
}
return paths;
}
public void SetLocalRadius(float radius)
{
localAreaRadius = radius;
localPlaces = ComputeLocalNodes(places, localAreaRadius);
}
public void MutatePath(List<int> path)
{
for (int i = 0; i < path.Count; i++)
{
if (rand.NextSingle() < mutationPickUniqueChance)
{
path[i] = PickUnqiueNode(rand, path, places.Count);
}
if (rand.NextSingle() < mutationSwapChance)
{
int idx1 = rand.Next(0, path.Count);
int idx2 = rand.Next(0, path.Count);
(path[idx2], path[idx1]) = (path[idx1], path[idx2]);
}
if (rand.NextSingle() < mutationPickLocalChance)
{
int localNode = PickUniqueLocalNode(rand, path, localPlaces[i]);
if (localNode != -1)
{
path[i] = localNode;
}
}
}
}
public void IterateSolution()
{
double bestScore = paths.First().Key;
double worstScore = paths.Last().Key;
var removedPaths = new List<double>();
var parentPool = new List<List<int>>();
foreach (var entry in paths)
{
double probability = (entry.Key - bestScore) / (worstScore - bestScore);
if (rand.NextSingle() >= probability)
{
parentPool.Add(entry.Value);
}
else
{
removedPaths.Add(entry.Key);
}
}
foreach (var key in removedPaths)
{
paths.Remove(key);
}
while (batchSize > paths.Count)
{
var parentIdx = rand.Next(0, parentPool.Count);
var parent = parentPool[parentIdx];
var child = new List<int>(parent);
MutatePath(child);
var cost = GetPathCost(child, places);
if (!paths.ContainsKey(cost))
{
paths.Add(cost, child);
}
}
iteration++;
}
public void IterateSolutionFor(float seconds)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
while (stopwatch.ElapsedMilliseconds < seconds * 1000)
{
IterateSolution();
}
stopwatch.Stop();
}
public Tuple<double, List<int>> GetBestPath()
{
var entry = paths.First();
return Tuple.Create(entry.Key, entry.Value);
}
}
}

BIN
Lab4/Ataskaita.docx Normal file

Binary file not shown.

94
Lab4/EditorWindow.cs Normal file
View File

@ -0,0 +1,94 @@
using System.Numerics;
using ImGuiNET;
using Lab4;
using Raylib_cs;
namespace Lab4
{
class EditorWindow
{
private bool showDemoWindow = false;
public ImFontPtr font1;
MainWindow win;
public EditorWindow(MainWindow win)
{
this.win = win;
}
public void Update(float dt)
{
ImGui.PushFont(font1);
if (showDemoWindow)
{
// Normally user code doesn't need/want to call this because positions are saved in .ini file anyway.
// Here we just want to make the demo initial state a bit more friendly!
ImGui.SetNextWindowPos(new Vector2(650, 20), ImGuiCond.FirstUseEver);
ImGui.ShowDemoWindow(ref showDemoWindow);
}
if (ImGui.Begin("Editor", ImGuiWindowFlags.None))
{
var algo = win.algo;
var mouse = win.GetMousePosition();
if (win.isIterationRunning)
{
if (ImGui.Button(win.iterationPaused ? "Continue" : "Pause"))
{
win.iterationPaused = !win.iterationPaused;
}
if (ImGui.Button("Stop"))
{
win.Stop();
}
} else
{
if (ImGui.Button("Start"))
{
win.Start();
}
}
ImGui.Text($"Mouse: {mouse.X:f3} {mouse.Y:f3}");
ImGui.Text($"Iteration: {algo.iteration}");
ImGui.Text($"Runtime: {win.interationTime:f3}s");
ImGuiInputTextFlags inputFlags = 0;
if (win.isIterationRunning)
{
uint bg = ImGui.GetColorU32(ImGuiCol.FrameBg);
ImGui.PushStyleColor(ImGuiCol.FrameBg, 0x44000000 | (bg & 0x00FFFFFF));
ImGui.PushStyleColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled));
inputFlags = ImGuiInputTextFlags.ReadOnly;
}
ImGui.InputInt("Seed", ref algo.seed, 1, 1, ImGuiInputTextFlags.CharsDecimal | inputFlags);
ImGui.InputInt("Batch size", ref algo.batchSize, 1, 1, ImGuiInputTextFlags.CharsDecimal | inputFlags);
if (win.isIterationRunning)
{
ImGui.PopStyleColor();
ImGui.PopStyleColor();
}
var localRadius = algo.localAreaRadius;
if (ImGui.InputFloat("Local area radius", ref localRadius))
{
Console.WriteLine(localRadius);
win.algo.SetLocalRadius(localRadius);
}
ImGui.SliderFloat("Mutation swap chance", ref algo.mutationSwapChance, 0, 0.3f);
ImGui.SliderFloat("Mutation repick chance", ref algo.mutationPickUniqueChance, 0, 0.3f);
ImGui.SliderFloat("Mutation repick local chance", ref algo.mutationPickLocalChance, 0, 0.3f);
ImGui.Text($"Best path score: {win.bestScore}");
ImGui.End();
}
ImGui.PopFont();
}
}
}

323
Lab4/ImguiController.cs Normal file
View File

@ -0,0 +1,323 @@
using ImGuiNET;
using Raylib_cs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
namespace Lab4
{
public class ImguiController : IDisposable
{
IntPtr context;
Texture2D fontTexture;
Vector2 scaleFactor = Vector2.One;
public ImguiController()
{
context = ImGui.CreateContext();
ImGui.SetCurrentContext(context);
}
public void Dispose()
{
ImGui.DestroyContext(context);
Raylib.UnloadTexture(fontTexture);
}
/// <summary>
/// Creates a texture and loads the font data from ImGui.
/// </summary>
public void Load(int width, int height)
{
ImGuiIOPtr io = ImGui.GetIO();
io.Fonts.AddFontDefault();
Resize(width, height);
LoadFontTexture();
SetupInput();
ImGui.NewFrame();
}
unsafe void LoadFontTexture()
{
ImGuiIOPtr io = ImGui.GetIO();
// Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders.
// If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
io.Fonts.GetTexDataAsRGBA32(out byte* pixels, out int width, out int height);
// Upload texture to graphics system
Image image = new Image
{
data = pixels,
width = width,
height = height,
mipmaps = 1,
format = PixelFormat.PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
};
fontTexture = Raylib.LoadTextureFromImage(image);
// Store texture id in imgui font
io.Fonts.SetTexID(new IntPtr(fontTexture.id));
// Clears font data on the CPU side
io.Fonts.ClearTexData();
}
void SetupInput()
{
ImGuiIOPtr io = ImGui.GetIO();
// Setup config flags
io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard;
// Setup back-end capabilities flags
io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors;
io.BackendFlags |= ImGuiBackendFlags.HasSetMousePos;
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[(int)ImGuiKey.Tab] = (int)KeyboardKey.KEY_TAB;
io.KeyMap[(int)ImGuiKey.LeftArrow] = (int)KeyboardKey.KEY_LEFT;
io.KeyMap[(int)ImGuiKey.RightArrow] = (int)KeyboardKey.KEY_RIGHT;
io.KeyMap[(int)ImGuiKey.UpArrow] = (int)KeyboardKey.KEY_UP;
io.KeyMap[(int)ImGuiKey.DownArrow] = (int)KeyboardKey.KEY_DOWN;
io.KeyMap[(int)ImGuiKey.PageUp] = (int)KeyboardKey.KEY_PAGE_UP;
io.KeyMap[(int)ImGuiKey.PageDown] = (int)KeyboardKey.KEY_PAGE_DOWN;
io.KeyMap[(int)ImGuiKey.Home] = (int)KeyboardKey.KEY_HOME;
io.KeyMap[(int)ImGuiKey.End] = (int)KeyboardKey.KEY_END;
io.KeyMap[(int)ImGuiKey.Insert] = (int)KeyboardKey.KEY_INSERT;
io.KeyMap[(int)ImGuiKey.Delete] = (int)KeyboardKey.KEY_DELETE;
io.KeyMap[(int)ImGuiKey.Backspace] = (int)KeyboardKey.KEY_BACKSPACE;
io.KeyMap[(int)ImGuiKey.Space] = (int)KeyboardKey.KEY_SPACE;
io.KeyMap[(int)ImGuiKey.Enter] = (int)KeyboardKey.KEY_ENTER;
io.KeyMap[(int)ImGuiKey.Escape] = (int)KeyboardKey.KEY_ESCAPE;
io.KeyMap[(int)ImGuiKey.A] = (int)KeyboardKey.KEY_A;
io.KeyMap[(int)ImGuiKey.C] = (int)KeyboardKey.KEY_C;
io.KeyMap[(int)ImGuiKey.V] = (int)KeyboardKey.KEY_V;
io.KeyMap[(int)ImGuiKey.X] = (int)KeyboardKey.KEY_X;
io.KeyMap[(int)ImGuiKey.Y] = (int)KeyboardKey.KEY_Y;
io.KeyMap[(int)ImGuiKey.Z] = (int)KeyboardKey.KEY_Z;
}
/// <summary>
/// Update imgui internals(input, frameData)
/// </summary>
/// <param name="dt"></param>
public void Update(float dt)
{
ImGuiIOPtr io = ImGui.GetIO();
io.DisplayFramebufferScale = Vector2.One;
io.DeltaTime = dt;
UpdateKeyboard();
UpdateMouse();
if (Raylib.IsWindowResized())
{
Resize(Raylib.GetScreenWidth(), Raylib.GetScreenHeight());
}
ImGui.NewFrame();
}
/// <summary>
/// Resize imgui display
/// </summary>
public void Resize(int width, int height)
{
ImGuiIOPtr io = ImGui.GetIO();
io.DisplaySize = new Vector2(width, height) / scaleFactor;
}
void UpdateKeyboard()
{
ImGuiIOPtr io = ImGui.GetIO();
// Modifiers are not reliable across systems
io.KeyCtrl = io.KeysDown[(int)KeyboardKey.KEY_LEFT_CONTROL] || io.KeysDown[(int)KeyboardKey.KEY_RIGHT_CONTROL];
io.KeyShift = io.KeysDown[(int)KeyboardKey.KEY_LEFT_SHIFT] || io.KeysDown[(int)KeyboardKey.KEY_RIGHT_SHIFT];
io.KeyAlt = io.KeysDown[(int)KeyboardKey.KEY_LEFT_ALT] || io.KeysDown[(int)KeyboardKey.KEY_RIGHT_ALT];
io.KeySuper = io.KeysDown[(int)KeyboardKey.KEY_LEFT_SUPER] || io.KeysDown[(int)KeyboardKey.KEY_RIGHT_SUPER];
// Key states
for (int i = (int)KeyboardKey.KEY_SPACE; i < (int)KeyboardKey.KEY_KB_MENU + 1; i++)
{
io.KeysDown[i] = Raylib.IsKeyDown((KeyboardKey)i);
}
// Key input
int keyPressed = Raylib.GetCharPressed();
if (keyPressed != 0)
{
io.AddInputCharacter((uint)keyPressed);
}
}
void UpdateMouse()
{
ImGuiIOPtr io = ImGui.GetIO();
// Store button states
for (int i = 0; i < io.MouseDown.Count; i++)
{
io.MouseDown[i] = Raylib.IsMouseButtonDown((MouseButton)i);
}
// Mouse scroll
io.MouseWheel += Raylib.GetMouseWheelMove();
// Mouse position
Vector2 mousePosition = io.MousePos;
bool focused = Raylib.IsWindowFocused();
if (focused)
{
if (io.WantSetMousePos)
{
Raylib.SetMousePosition((int)mousePosition.X, (int)mousePosition.Y);
}
else
{
io.MousePos = Raylib.GetMousePosition();
}
}
// Mouse cursor state
if ((io.ConfigFlags & ImGuiConfigFlags.NoMouseCursorChange) == 0 || Raylib.IsCursorHidden())
{
ImGuiMouseCursor cursor = ImGui.GetMouseCursor();
if (cursor == ImGuiMouseCursor.None || io.MouseDrawCursor)
{
Raylib.HideCursor();
}
else
{
Raylib.ShowCursor();
}
}
}
/// <summary>
/// Gets the geometry as set up by ImGui and sends it to the graphics device
/// </summary>
public void Draw()
{
ImGui.Render();
RenderCommandLists(ImGui.GetDrawData());
}
// Returns a Color struct from hexadecimal value
Color GetColor(uint hexValue)
{
Color color;
color.r = (byte)(hexValue & 0xFF);
color.g = (byte)((hexValue >> 8) & 0xFF);
color.b = (byte)((hexValue >> 16) & 0xFF);
color.a = (byte)((hexValue >> 24) & 0xFF);
return color;
}
void DrawTriangleVertex(ImDrawVertPtr idxVert)
{
Color c = GetColor(idxVert.col);
Rlgl.rlColor4ub(c.r, c.g, c.b, c.a);
Rlgl.rlTexCoord2f(idxVert.uv.X, idxVert.uv.Y);
Rlgl.rlVertex2f(idxVert.pos.X, idxVert.pos.Y);
}
// Draw the imgui triangle data
void DrawTriangles(uint count, int idxOffset, int vtxOffset, ImVector<ushort> idxBuffer, ImPtrVector<ImDrawVertPtr> idxVert, IntPtr textureId)
{
ushort index = 0;
ImDrawVertPtr vertex;
if (Rlgl.rlCheckRenderBatchLimit((int)count * 3))
{
Rlgl.rlDrawRenderBatchActive();
}
Rlgl.rlBegin(DrawMode.TRIANGLES);
Rlgl.rlSetTexture((uint)textureId);
for (int i = 0; i <= (count - 3); i += 3)
{
index = idxBuffer[idxOffset + i];
vertex = idxVert[vtxOffset + index];
DrawTriangleVertex(vertex);
index = idxBuffer[idxOffset + i + 1];
vertex = idxVert[vtxOffset + index];
DrawTriangleVertex(vertex);
index = idxBuffer[idxOffset + i + 2];
vertex = idxVert[vtxOffset + index];
DrawTriangleVertex(vertex);
}
Rlgl.rlEnd();
}
void RenderCommandLists(ImDrawDataPtr data)
{
// Scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fbWidth = (int)(data.DisplaySize.X * data.FramebufferScale.X);
int fbHeight = (int)(data.DisplaySize.Y * data.FramebufferScale.Y);
// Avoid rendering if display is minimized or if the command list is empty
if (fbWidth <= 0 || fbHeight <= 0 || data.CmdListsCount == 0)
{
return;
}
Rlgl.rlDrawRenderBatchActive();
Rlgl.rlDisableBackfaceCulling();
Rlgl.rlEnableScissorTest();
data.ScaleClipRects(ImGui.GetIO().DisplayFramebufferScale);
for (int n = 0; n < data.CmdListsCount; n++)
{
int idxOffset = 0;
ImDrawListPtr cmdList = data.CmdListsRange[n];
// Vertex buffer and index buffer generated by DearImGui
ImPtrVector<ImDrawVertPtr> vtxBuffer = cmdList.VtxBuffer;
ImVector<ushort> idxBuffer = cmdList.IdxBuffer;
for (int cmdi = 0; cmdi < cmdList.CmdBuffer.Size; cmdi++)
{
ImDrawCmdPtr pcmd = cmdList.CmdBuffer[cmdi];
// Scissor rect
Vector2 pos = data.DisplayPos;
int rectX = (int)((pcmd.ClipRect.X - pos.X) * data.FramebufferScale.X);
int rectY = (int)((pcmd.ClipRect.Y - pos.Y) * data.FramebufferScale.Y);
int rectW = (int)((pcmd.ClipRect.Z - rectX) * data.FramebufferScale.Y);
int rectH = (int)((pcmd.ClipRect.W - rectY) * data.FramebufferScale.Y);
Rlgl.rlScissor(rectX, Raylib.GetScreenHeight() - (rectY + rectH), rectW, rectH);
if (pcmd.UserCallback != IntPtr.Zero)
{
// pcmd.UserCallback(cmdList, pcmd);
idxOffset += (int)pcmd.ElemCount;
}
else
{
DrawTriangles(pcmd.ElemCount, idxOffset, (int)pcmd.VtxOffset, idxBuffer, vtxBuffer, pcmd.TextureId);
idxOffset += (int)pcmd.ElemCount;
Rlgl.rlDrawRenderBatchActive();
}
}
}
Rlgl.rlSetTexture(0);
Rlgl.rlDisableScissorTest();
Rlgl.rlEnableBackfaceCulling();
}
}
}

View File

@ -7,6 +7,7 @@
<Nullable>enable</Nullable>
<PlatformTarget>x64</PlatformTarget>
<ProduceReferenceAssembly>False</ProduceReferenceAssembly>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@ -21,6 +22,8 @@
<None Update="places.tsv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<PackageReference Include="Raylib-cs" Version="4.5.0" />
<PackageReference Include="ImGui.NET" Version="1.78.0" />
</ItemGroup>
</Project>

198
Lab4/MainWindow.cs Normal file
View File

@ -0,0 +1,198 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Raylib_cs;
using static Lab4.Algorithm;
namespace Lab4
{
public class MainWindow
{
public Algorithm algo;
List<Place> places;
public int screenWidth;
public int screenHeight;
public float interationTime = 0;
Task? iterationTask;
public List<int>? bestPath;
public double bestScore = 0;
public bool isIterationRunning = false;
public bool iterationPaused = false;
RaylibDrawContext ctx;
ScoreHistWindow scoreHist;
public MainWindow(int screenWidth, int screenHeight, List<Place> places)
{
this.places = places;
algo = new Algorithm(places);
scoreHist = new ScoreHistWindow(this);
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
{
ctx = new RaylibDrawContext();
const int screenMargin = 50;
var (minX, maxX, minY, maxY) = GetCoordinateBounds(places);
var rangeX = maxX - minX;
var rangeY = maxY - minY;
float scale = Math.Min((screenWidth - screenMargin * 2) / rangeX, (screenHeight - screenMargin * 2) / rangeY);
ctx.Scale(scale, scale);
ctx.Translate(-screenMargin, -screenMargin);
ctx.Translate(minX * scale, minY * scale);
}
}
public void Start()
{
algo.Reset();
scoreHist.Reset();
isIterationRunning = true;
iterationPaused = false;
interationTime = 0;
iterationTask = new Task(() => {
while (isIterationRunning)
{
if (!iterationPaused)
{
algo.IterateSolution();
var best = algo.GetBestPath();
bestScore = best.Item1;
bestPath = best.Item2;
}
}
});
iterationTask.Start();
}
public void Stop()
{
isIterationRunning = false;
}
public static Tuple<float, float, float, float> GetCoordinateBounds(List<Place> places)
{
float minX = places[0].x;
float maxX = places[0].x;
float minY = places[0].y;
float maxY = places[0].y;
for (int i = 1; i < places.Count; i++)
{
minX = Math.Min(minX, places[i].x);
maxX = Math.Max(maxX, places[i].x);
minY = Math.Min(minY, places[i].y);
maxY = Math.Max(maxY, places[i].y);
}
return Tuple.Create(minX, maxX, minY, maxY);
}
public static void DrawPath(RaylibDrawContext ctx, List<int> path, List<Place> places)
{
Color color = Color.GREEN;
for (int i = 0; i < path.Count - 1; i++)
{
var place0 = places[path[i + 0]];
var place1 = places[path[i + 1]];
ctx.DrawLine(place0.x, place0.y, place1.x, place1.y, color);
}
}
public Vector2 GetMousePosition()
{
return ctx.TransformToLocalSpace(Raylib.GetMousePosition());
}
public Place? GetNearestPlaceToMouse()
{
if (places.Count == 0) return null;
var mouse = GetMousePosition();
var mearest = places.First();
double nearestDistance = GetDistance(mearest.x, mearest.y, mouse.X, mouse.Y);
foreach (var place in places)
{
var distance = GetDistance(place.x, place.y, mouse.X, mouse.Y);
if (distance < nearestDistance)
{
nearestDistance = distance;
mearest = place;
}
}
return mearest;
}
public unsafe void Run()
{
Raylib.SetTraceLogCallback(&Logging.LogConsole);
Raylib.SetConfigFlags(ConfigFlags.FLAG_VSYNC_HINT | ConfigFlags.FLAG_WINDOW_RESIZABLE);
Raylib.InitWindow(this.screenWidth, this.screenHeight, "IP Užduotis");
Raylib.SetTargetFPS(60);
Raylib.InitAudioDevice();
ImguiController controller = new ImguiController();
EditorWindow editor = new EditorWindow(this);
controller.Load(screenWidth, screenHeight);
while (!Raylib.WindowShouldClose())
{
// Update
float dt = Raylib.GetFrameTime();
controller.Update(dt);
editor.Update(dt);
scoreHist.Update(dt);
if (isIterationRunning && !iterationPaused)
{
interationTime += dt;
}
// Draw
Raylib.BeginDrawing();
Raylib.ClearBackground(Color.RAYWHITE);
foreach (var place in places)
{
ctx.DrawCircle(place.x, place.y, 2000, Color.RED);
}
if (bestPath != null)
{
DrawPath(ctx, bestPath, places);
}
/*
var nearest = GetNearestPlaceToMouse();
if (nearest != null)
{
var mouse = GetMousePosition();
ctx.DrawLine(nearest.x, nearest.y, mouse.X, mouse.Y, Color.BLUE);
ctx.DrawCircleLines(nearest.x, nearest.y, algo.localAreaRadius, Color.BLUE);
}
*/
controller.Draw();
Raylib.EndDrawing();
}
// De-Initialization
controller.Dispose();
Raylib.CloseAudioDevice();
Raylib.CloseWindow();
Stop();
}
}
}

View File

@ -1,197 +1,27 @@
using System.Diagnostics;
using System.IO;
using Microsoft.VisualBasic;
using Raylib_cs;
using System.Diagnostics;
namespace Lab4
{
class Place
{
public string name;
public float x;
public float y;
public Place(string name, float x, float y)
{
this.name = name;
this.x = x;
this.y = y;
}
}
internal class Program
{
public static double GetDistance(Place A, Place B)
static unsafe void Main(string[] args)
{
float dx = A.x - B.x;
float dy = A.y - B.y;
return Math.Sqrt(dx * dx + dy * dy);
}
var places = Algorithm.ReadPlacesFromTSV("places.tsv");
public static double GetTravalCost(Place A, Place B)
{
return Math.Sqrt(GetDistance(A, B));
}
var win = new MainWindow(1280, 720, places);
win.Run();
public static List<Place> ReadPlacesFromTSV(string filename)
{
List<Place> cities = new List<Place>();
foreach (var line in File.ReadLines(filename))
{
string[] parts = line.Split("\t");
string name = parts[0];
float x = float.Parse(parts[2]);
float y = float.Parse(parts[3]);
cities.Add(new Place(name, x, y));
}
return cities;
}
// Warning 'PickUnqiueNode' could get stuck in an infinite loop, if there are not enough places
public static int PickUnqiueNode(Random rand, List<int> path, int placesCount)
{
while (true)
{
int num = rand.Next(0, placesCount);
if (!path.Contains(num))
{
return num;
}
}
}
public static List<int> GenerateRandomPath(Random rand, int size, int placesCount)
{
List<int> path = new List<int>(size);
for (int i = 0; i < size; i++)
{
path.Add(PickUnqiueNode(rand, path, placesCount));
}
return path;
}
public static double GetPathCost(List<int> path, List<Place> places)
{
double cost = 0;
for (int i = 0; i < path.Count-1; i++)
{
int from = path[i];
int to = path[i+1];
cost += GetTravalCost(places[from], places[to]);
}
cost += GetTravalCost(places[path.First()], places[path.Last()]);
return cost;
}
public static List<int> SplicePaths(Random rand, List<int> A, List<int> B)
{
Debug.Assert(A.Count == B.Count);
List<int> spliced = new List<int>(A.Count);
for (int i = 0; i < A.Count-1; i++)
{
spliced[i] = rand.Next(0, 2) == 0 ? A[i] : B[i];
}
return spliced;
}
public static void MutatePath(Random rand, List<int> path, float mutationPickUniqueChance, float mutationSwapChance, int placesCount)
{
for (int i = 0; i < path.Count; i++)
{
if (rand.NextSingle() < mutationPickUniqueChance) {
path[i] = PickUnqiueNode(rand, path, placesCount);
}
if (rand.NextSingle() < mutationSwapChance) {
int idx1 = rand.Next(0, path.Count);
int idx2 = rand.Next(0, path.Count);
(path[idx2], path[idx1]) = (path[idx1], path[idx2]);
}
}
}
static void Main(string[] args)
{
/*
Faile places_data.xlsx pateikta informacija apie lankytinas vietas (1 lentelė). Tikslas: kaip galima pigesnio maršruto sudarymas kai,
* priimama, kad kelionės tarp vietų kaina lygi kvadratinei šakniai kelionės atstumo;
* kelionės pradžios ir pabaigos vieta sutampa (su grįžimu atgal);
* ta pati vieta negali būti aplankyta daugiau nei vieną;
* Reikia aplankyti bet kurias 150 vietų pateikto sąrašo.
*/
int pathLength = 150;
int batchSize = 50;
int timeLimitMs = 60 * 1000;
float mutationPickUniqueChance = 0.05f;
float mutationSwapChance = 0.05f;
int seed = (int)DateTime.Now.Ticks;
Random rand = new Random(seed);
var places = ReadPlacesFromTSV("places.tsv");
var bestPaths = new SortedList<double, List<int>>();
for (int i = 0; i < batchSize; i++)
{
var path = GenerateRandomPath(rand, pathLength, places.Count);
var cost = GetPathCost(path, places);
bestPaths.Add(cost, path);
}
var stopwatch = new Stopwatch();
int iteration = 0;
stopwatch.Start();
while (stopwatch.ElapsedMilliseconds < timeLimitMs)
{
double bestScore = bestPaths.First().Key;
double worstScore = bestPaths.Last().Key;
if (iteration % 200 == 0)
{
//Console.WriteLine("At {0}, best '{1}', worst '{2}'", iteration, bestScore, worstScore);
}
var removedPaths = new List<double>();
var parentPool = new List<List<int>>();
foreach (var entry in bestPaths)
{
double probability = (entry.Key - bestScore) / (worstScore - bestScore);
if (rand.NextSingle() >= probability) {
parentPool.Add(entry.Value);
} else {
removedPaths.Add(entry.Key);
}
}
foreach (var key in removedPaths)
{
bestPaths.Remove(key);
}
while (batchSize > bestPaths.Count)
{
var parentIdx = rand.Next(0, parentPool.Count);
var parent = parentPool[parentIdx];
var child = new List<int>(parent);
MutatePath(rand, child, mutationPickUniqueChance, mutationSwapChance, places.Count);
var cost = GetPathCost(child, places);
if (!bestPaths.ContainsKey(cost)) {
bestPaths.Add(cost, child);
}
}
iteration++;
}
stopwatch.Stop();
Console.WriteLine("Best after {0} iterations is {1}", iteration, bestPaths.First().Key);
Console.WriteLine("Time taken: {0}ms", stopwatch.ElapsedMilliseconds);
Console.WriteLine("Seed: {0}", seed);
//var algo = new Algorithm(places);
//algo.IterateSolutionFor(1);
//Console.WriteLine("Best after {0} iterations is {1}", algo.iteration, algo.GetBestPath().Item1);
//Console.WriteLine("Seed: {0}", algo.seed);
// Best after 484012 iterations is 30826.444176719036
// Time taken: 60000ms
// Seed: -1477536140
}
}
}

98
Lab4/RaylibDrawContext.cs Normal file
View File

@ -0,0 +1,98 @@
using Raylib_cs;
using System.Numerics;
using System.Security.Cryptography;
using System.Xml;
namespace Lab4
{
public class RaylibDrawContext
{
class DrawFrame
{
public float ox = 0;
public float oy = 0;
public float sx = 1;
public float sy = 1;
public DrawFrame(float ox, float oy, float sx, float sy)
{
this.ox = ox;
this.oy = oy;
this.sx = sx;
this.sy = sy;
}
}
public float ox = 0;
public float oy = 0;
public float sx = 1;
public float sy = 1;
Stack<DrawFrame> stack;
public RaylibDrawContext()
{
stack = new Stack<DrawFrame>();
}
public void Translate(float offsetX, float offsetY)
{
ox += offsetX / sx;
oy += offsetY / sy;
}
public void Scale(float scaleX, float scaleY)
{
sx *= scaleX;
sy *= scaleY;
}
public void Push()
{
stack.Push(new DrawFrame(ox, oy, sx, sy));
}
public void Pop()
{
var frame = stack.Pop();
ox = frame.ox;
oy = frame.oy;
sx = frame.sx;
sy = frame.sy;
}
public Vector2 TransformToLocalSpace(Vector2 pos)
{
return new Vector2(
(pos.X / sx) + ox,
(pos.Y / sy) + oy
);
}
public void DrawCircle(float centerX, float centerY, float radius, Color color)
{
int x = (int)((centerX - ox) * sx);
int y = (int)((centerY - oy) * sy);
float rh = radius * sx;
float rv = radius * sy;
Raylib.DrawEllipse(x, y, rh, rv, color);
}
public void DrawCircleLines(float centerX, float centerY, float radius, Color color)
{
int x = (int)((centerX - ox) * sx);
int y = (int)((centerY - oy) * sy);
float rh = radius * sx;
float rv = radius * sy;
Raylib.DrawEllipseLines(x, y, rh, rv, color);
}
public void DrawLine(float startPosX, float startPosY, float endPosX, float endPosY, Color color)
{
int x0 = (int)((startPosX - ox) * sx);
int y0 = (int)((startPosY - oy) * sy);
int x1 = (int)((endPosX - ox) * sx);
int y1 = (int)((endPosY - oy) * sy);
Raylib.DrawLine(x0, y0, x1, y1, color);
}
}
}

59
Lab4/ScoreHistWindow.cs Normal file
View File

@ -0,0 +1,59 @@
using ImGuiNET;
namespace Lab4
{
internal class ScoreHistWindow
{
public ImFontPtr font1;
MainWindow win;
float[] dataPoints;
float timer = 0;
float lastSampleTime = -1;
float sampleInterval = 0.1f;
public ScoreHistWindow(MainWindow win)
{
this.win = win;
dataPoints = new float[100];
}
public void Reset()
{
dataPoints = new float[100];
timer = 0;
lastSampleTime = -1;
}
public unsafe void Update(float dt)
{
if (win.isIterationRunning && !win.iterationPaused)
{
timer += dt;
if (timer - lastSampleTime > sampleInterval)
{
for (int i = 0; i < dataPoints.Length-1; i++)
{
dataPoints[i] = dataPoints[i + 1];
}
dataPoints[dataPoints.Length - 1] = (float)win.bestScore;
lastSampleTime = timer;
}
}
ImGui.PushFont(font1);
if (ImGui.Begin("Score histogram", ImGuiWindowFlags.None))
{
ImGui.PlotLines("", ref dataPoints[0], dataPoints.Length, 0, null, 10_000f, 50_000f, new System.Numerics.Vector2(0, 180.0f));
ImGui.Spacing();
ImGui.End();
}
ImGui.PopFont();
}
}
}