Skip to content

Commit

Permalink
Initial code commit
Browse files Browse the repository at this point in the history
  • Loading branch information
kntajus committed Jun 2, 2021
1 parent d12b9eb commit 00e7815
Show file tree
Hide file tree
Showing 15 changed files with 821 additions and 0 deletions.
25 changes: 25 additions & 0 deletions Witness.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30804.86
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Witness", "Witness\Witness.csproj", "{8A99A618-B926-4ECC-8DF8-75C2454B794D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8A99A618-B926-4ECC-8DF8-75C2454B794D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A99A618-B926-4ECC-8DF8-75C2454B794D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A99A618-B926-4ECC-8DF8-75C2454B794D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A99A618-B926-4ECC-8DF8-75C2454B794D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {47CF38CE-A5CA-42C0-8124-7FC740384FE0}
EndGlobalSection
EndGlobal
76 changes: 76 additions & 0 deletions Witness/ColorPuzzle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.Collections.Generic;

namespace Witness
{
public class ColorPuzzle : Puzzle<Edge>
{
private readonly HashSet<Edge> _edges = new HashSet<Edge>();
private readonly Rgba32[] _colors = new[] {
new Rgba32(16, 151, 122), // Puzzle background
new Rgba32(25, 56, 44), // Black box
new Rgba32(200, 196, 189), // White box
new Rgba32(139, 143, 75), // Green box
new Rgba32(141, 24, 173) // Purple box
};

public ColorPuzzle(Region region, Image<Rgba32> image) : base(region, image) { }

protected override string Description => "Color";

protected override int GetCellValue(Point pixel)
{
var minDistance = int.MaxValue;
var colorId = -1;
for (int i = 0; i < _colors.Length; i++)
{
var distance = GetDistance(_colors[i], Image[pixel.X, pixel.Y]);
if (distance < minDistance)
{
minDistance = distance;
colorId = i;
}
}

return colorId;
}

protected override bool ValidateCell(ICell cell) => ValidateColors(cell, new HashSet<ICell>(), new HashSet<int>());

protected override Edge AddingPathSegment(IVertex from, IVertex to)
{
var edge = new Edge(Math.Min(from.X, to.X), Math.Min(from.Y, to.Y), Math.Max(from.X, to.X), Math.Max(to.Y, from.Y));
_edges.Add(edge);
return edge;
}

protected override void RemovingPathSegment(Edge edge) => _edges.Remove(edge);

private bool ValidateColors(ICell cell, ISet<ICell> visitedCells, ISet<int> colors)
{
if (visitedCells.Contains(cell))
return true;
visitedCells.Add(cell);

if (cell.Value > 0)
colors.Add(cell.Value);
// More than one color found in current area, this is not a valid path
if (colors.Count > 1)
return false;

foreach (var neighbor in cell.GetNeighbors())
{
var edge = new Edge(Math.Max(cell.X, neighbor.X), Math.Max(cell.Y, neighbor.Y), Math.Min(cell.X, neighbor.X) + 1, Math.Min(cell.Y, neighbor.Y) + 1);
if (!_edges.Contains(edge))
// We're not blocked from this adjacent cell by the proposed path, so validate its color
if (!ValidateColors(neighbor, visitedCells, colors))
return false;
}
return true;
}

private static int GetDistance(Rgba32 a, Rgba32 b) => (a.R - b.R) * (a.R - b.R) + (a.G - b.G) * (a.G - b.G) + (a.B - b.B) * (a.B - b.B);
}
}
23 changes: 23 additions & 0 deletions Witness/ConnectedPoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using SixLabors.ImageSharp;

namespace Witness
{
public struct ConnectedPoint
{
private readonly Point _point;

public ConnectedPoint(Point point, byte intensity, byte parentIntensity)
{
_point = point;
Intensity = intensity;
ParentIntensity = parentIntensity;
}

public int X => _point.X;
public int Y => _point.Y;
public byte Intensity { get; }
public byte ParentIntensity { get; }

public static explicit operator Point(ConnectedPoint cp) => cp._point;
}
}
21 changes: 21 additions & 0 deletions Witness/Edge.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using SixLabors.ImageSharp;
using System;

namespace Witness
{
public class Edge
{
public Edge(int minX, int minY, int maxX, int maxY)
{
Point1 = new Point(minX, minY);
Point2 = new Point(maxX, maxY);
}

public Point Point1 { get; }
public Point Point2 { get; }

public override bool Equals(object obj) => obj is Edge edge && Point1.Equals(edge.Point1) && Point2.Equals(edge.Point2);

public override int GetHashCode() => HashCode.Combine(Point1, Point2);
}
}
69 changes: 69 additions & 0 deletions Witness/FileProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using SixLabors.ImageSharp;
using System;
using System.IO;
using System.Threading;

namespace Witness
{
public class FileProcessor
{
private readonly PuzzleFinder _finder = new PuzzleFinder();
private readonly string _outPath;
private FileSystemWatcher _watcher;
private DateTime _lastCreated = DateTime.UtcNow;

public FileProcessor(string inPath, string outPath)
{
_outPath = outPath;
_watcher = new FileSystemWatcher(inPath);
_watcher.Created += Watcher_Created;
}

public void Start() => _watcher.EnableRaisingEvents = true;

private void Watcher_Created(object sender, FileSystemEventArgs e)
{
// FileSystemWatcher is pants and sometimes raises multiple events for the same file, skip if too soon
if (DateTime.UtcNow - _lastCreated < TimeSpan.FromSeconds(1))
return;
_lastCreated = DateTime.UtcNow;

try
{
var puzzle = GetPuzzle(e.FullPath);
if (puzzle != null)
{
puzzle.WriteToConsole();
var solution = puzzle.Solve();
var illustrator = new PuzzleIllustrator(puzzle);
var image = illustrator.Illustrate(solution);
image.SaveAsPng(Path.Combine(_outPath, "SOLVED_" + e.Name));
}
Console.WriteLine("============================");
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}

private IPuzzle GetPuzzle(string imagePath)
{
IOException ioex = null;
for (var i = 0; i < 10; i++)
{
try
{
return _finder.GetPuzzle(imagePath);
}
catch (IOException ex)
{
ioex = ex;
// FileSystemWatcher being pants again - image is most likely still being written to disk, try again shortly
Thread.Sleep(100);
}
}
throw ioex;
}
}
}
10 changes: 10 additions & 0 deletions Witness/PointExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using SixLabors.ImageSharp;

namespace Witness
{
public static class PointExtensions
{
public static Point[] GetNeighbors(this Point p) =>
new[] { new Point(p.X + 1, p.Y), new Point(p.X, p.Y - 1), new Point(p.X - 1, p.Y), new Point(p.X, p.Y + 1) };
}
}
21 changes: 21 additions & 0 deletions Witness/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

namespace Witness
{
public class Program
{
public static void Main(string[] args)
{
var screenshotFolderPath = args[0];
var outFolderPath = args[1];
Console.WriteLine("Monitoring: " + screenshotFolderPath);
Console.WriteLine("Solutions saved to: " + outFolderPath);
Console.WriteLine("Press any key to exit.");

var processor = new FileProcessor(screenshotFolderPath, outFolderPath);
processor.Start();

Console.ReadKey();
}
}
}
7 changes: 7 additions & 0 deletions Witness/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"profiles": {
"Witness": {
"commandName": "Project"
}
}
}
Loading

0 comments on commit 00e7815

Please sign in to comment.