using System.Text;
using HtmlAgilityPack;
namespace ColourFlood;
class Program
{
public static void ColourToConsoleColor(Colour colour)
{
Console.BackgroundColor = colour switch
{
Colour.Green => ConsoleColor.Green,
Colour.Orange => ConsoleColor.Red,
Colour.Blue => ConsoleColor.Blue,
Colour.Purple => ConsoleColor.DarkMagenta,
Colour.Teal => ConsoleColor.Cyan,
};
}
static void Main(string[] args)
{
var doc = new HtmlDocument();
doc.Load("test.html");
Grid original = new PonderClub().From(doc);
Colour[]? best = null;
PriorityQueue<(Colour[] history, Grid grid), int> queue = new PriorityQueue<(Colour[] history, Grid grid), int>();
queue.Enqueue(([], original), -original.Score);
while (queue.Count > 0)
{
var queueItem = queue.Dequeue();
handleQueueItem(queueItem.history, queueItem.grid);
}
PrintHistory(best);
void handleQueueItem(Colour[] history, Grid grid)
{
foreach (var choice in grid.Possibilities)
{
var newGrid = new Grid(grid);
Colour[] newhistory = [..history, choice];
var captures = newGrid.Capture(choice);
newGrid.SwitchColour(choice, captures);
if (newGrid.Done)
{
if (best != null && newhistory.Length >= best.Length) return;
best = newhistory;
PrintHistory(best);
Task.Run(() =>
{
foreach (var item in queue.UnorderedItems
.Where(item => item.Element.history.Length >= best.Length)
.ToArray())
{
queue.Remove(item.Element, out _, out _);
}
queue.TrimExcess();
});
return;
}
if (best != null && newhistory.Length >= best.Length) return;
queue.Enqueue((newhistory, newGrid), -newGrid.Score - captures.Count + newhistory.Length);
}
}
}
private static void PrintHistory(Colour[]? history)
{
Console.Write($"{history.Length}: ");
foreach (var pick in history)
{
ColourToConsoleColor(pick);
Console.Write(" ");
}
Console.ResetColor();
Console.WriteLine();
}
}
interface Import
{
Grid From(HtmlDocument doc);
}
class PonderClub : Import
{
public Grid From(HtmlDocument doc)
{
var gridNode = doc.DocumentNode.SelectSingleNode("""//*[@id="root"]/div/div/div[1]/div/div/div[2]/div/div""");
var init = gridNode.ChildNodes.Select(cellNode => cellNode.FirstChild.GetClasses().Last() switch
{
"css-128i23a" => Colour.Green,
"css-1husk6m" => Colour.Orange,
"css-1nbkhyv" => Colour.Blue,
"css-1wib823" => Colour.Purple,
"css-tolu00" => Colour.Teal,
}).ToArray();
return new Grid((short)Math.Sqrt(init.Length), init);
}
}
enum Colour : byte
{
Green,
Orange,
Blue,
Purple,
Teal
}
record struct Pos(short X, short Y)
{
public Pos[] Neighbors(short width, short height)
{
List neighbors = new List(4);
if (Y != 0)
{
neighbors.Add(this with { Y = (short)(Y - 1) });
}
if (X != 0)
{
neighbors.Add(this with { X = (short)(X - 1) });
}
if (X != width - 1)
{
neighbors.Add(this with { X = (short)(X + 1) });
}
if (Y != height - 1)
{
neighbors.Add(this with { Y = (short)(Y + 1) });
}
return neighbors.ToArray();
}
}
record struct Grid
{
private static short width;
private static short height;
private Colour? current;
private static readonly Dictionary cells = new Dictionary();
private HashSet captured;
public bool Done => cells.Count == captured.Count;
public int Score => captured.Count;
public IEnumerable Possibilities
{
get
{
var current = this.current;
var captured = this.captured;
return cells
.Where(kv => kv.Value != current && !captured.Contains(kv.Key))
.Select(kv => kv.Value)
.Distinct();
}
}
public Grid(Grid original)
{
current = original.current;
captured = [..original.captured];
}
public Grid(short size, Colour[] init) : this(size, size, init) { }
public Grid(short width, short height, Colour[] init)
{
Grid.width = width;
Grid.height = height;
if (width * height != init.Length)
{
throw new InvalidOperationException("Invalid init");
}
captured = [new (0, 0)];
var index = 0;
for (short y = 0; y < height; y++)
{
for (short x = 0; x < width; x++)
{
cells.Add(new(x, y), init[index++]);
}
}
var captures = Capture(init[0]);
SwitchColour(init[0], captures);
}
public void SwitchColour(Colour newColour, HashSet newCaptures)
{
current = newColour;
captured.UnionWith(newCaptures);
}
public HashSet Capture(Colour newColour)
{
if (current == newColour) return [];
HashSet newCaptures = [];
Queue queue = new Queue(captured);
while (queue.Count > 0)
{
var cell = queue.Dequeue();
foreach (var neighbor in cell.Neighbors(width, height))
{
if (captured.Contains(neighbor) || newCaptures.Contains(neighbor)) continue;
if (cells[neighbor] == newColour)
{
newCaptures.Add(neighbor);
queue.Enqueue(neighbor);
}
}
}
return newCaptures;
}
public void Print()
{
for (short y = 0; y <= height-1; y++)
{
for (short x = 0; x <= width-1; x++)
{
var cell = new Pos(x, y);
var colour = cells[cell];
if (captured.Contains(cell) && current != null)
{
colour = current.Value;
}
Program.ColourToConsoleColor(colour);
Console.Write(new string(' ', 2));
}
Console.ResetColor();
Console.WriteLine();
}
Console.WriteLine();
}
}