Skip to content

Commit

Permalink
bfs to sequence generator
Browse files Browse the repository at this point in the history
  • Loading branch information
mazharenko committed Dec 22, 2024
1 parent 2100777 commit 30d27c3
Show file tree
Hide file tree
Showing 13 changed files with 214 additions and 257 deletions.
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="FluentAssertions" Version="6.12.2" />
<PackageVersion Include="mazharenko.AocAgent" Version="7.0.0" />
<PackageVersion Include="mazharenko.AocAgent" Version="7.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageVersion Include="Microsoft.Z3" Version="4.12.2" />
<PackageVersion Include="morelinq" Version="4.3.0" />
Expand Down
37 changes: 0 additions & 37 deletions src/aoc/Common/Bfs/Bfs.cs

This file was deleted.

45 changes: 0 additions & 45 deletions src/aoc/Common/Bfs/Builders.cs

This file was deleted.

60 changes: 0 additions & 60 deletions src/aoc/Common/Bfs/Folder.cs

This file was deleted.

22 changes: 0 additions & 22 deletions src/aoc/Common/Bfs/Search.cs

This file was deleted.

45 changes: 45 additions & 0 deletions src/aoc/Common/Search/Bfs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.Collections;

namespace aoc.Common.Search;

public static class Bfs
{
public static BfsBuilder<T, T> StartWith<T>(T start)
{
return new BfsBuilder<T, T>(start, x => x);
}
}

public class Bfs<T, TVisitedKey>(T start, IAdjacency<T> adjacency, Func<T, TVisitedKey>? visitedKey) : IEnumerable<Path<T>>
{
public IEnumerator<Path<T>> GetEnumerator()
{
var visited = new HashSet<TVisitedKey?>();
if (visitedKey is not null)
visited.Add(visitedKey(start));
var queue = new PriorityQueue<Path<T>, int>();
queue.Enqueue(new Path<T>(L.Singleton(new PathItem<T>(start, 0))), int.MaxValue);
while (true)
{
if (queue.Count == 0) yield break;
var currentPath = queue.Dequeue();

yield return currentPath;

var current = currentPath.PathList.Head;
var adjacent = adjacency.GetAdjacent(current.Item);
foreach (var (adjacentValue, weight) in adjacent)
{
if (visitedKey is null || visited.Add(visitedKey(adjacentValue)))
queue.Enqueue(new Path<T>(currentPath.PathList.Prepend(new PathItem<T>(adjacentValue, current.Len + weight))), current.Len + weight);
}
}
}

// todo: Enumerable<T>? Enumerable<PathItem<T>>?

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
23 changes: 23 additions & 0 deletions src/aoc/Common/Search/Builders.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace aoc.Common.Search;

public class BfsBuilder<T, TVisitedKey>(T start, Func<T, TVisitedKey>? visitedKeyFunction)
{
public BfsBuilder<T, TNewKey> WithVisitedKey<TNewKey>(Func<T, TNewKey> visited)
{
return new BfsBuilder<T, TNewKey>(start, visited);
}

public BfsBuilder<T, object> WithoutTrackingVisited()
{
return new BfsBuilder<T, object>(start, null);
}

public Bfs<T, TVisitedKey> WithAdjacency(Func<T, (T newState, int weight)[]> adjacency)
=> WithAdjacency(new AdhocAdjacency<T>(adjacency));

public Bfs<T, TVisitedKey> WithAdjacency(Func<T, IEnumerable<T>> adjacency)
=> WithAdjacency(new AdhocAdjacency<T>(state => adjacency(state).Select(newState => (newState, 1))));

public Bfs<T, TVisitedKey> WithAdjacency(IAdjacency<T> adjacency)
=> new(start, adjacency, visitedKeyFunction);
}
56 changes: 56 additions & 0 deletions src/aoc/Common/Search/Common.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
namespace aoc.Common.Search;

public readonly record struct PathItem<T>(T Item, int Len);

public readonly record struct Path<T>(L<PathItem<T>> PathList)
{
public T HeadItem { get; } = PathList.Head.Item;
public int Len => PathList.Head.Len;
}

public abstract record Result<T>;

public static class ResultExtensions
{
public static TRes Match<T, TRes>(this Result<T> result, Func<Path<T>, TRes> onFound, Func<TRes> onNotFound)
{
return result switch
{
Found<T> found => onFound(found.Path),
NotFound<T> => onNotFound(),
_ => throw new ArgumentOutOfRangeException(nameof(result))
};
}

public static void Match<T>(this Result<T> result, Action<Path<T>> onFound, Action onNotFound)
{
switch (result)
{
case Found<T> found:
onFound(found.Path);
break;
case NotFound<T>:
onNotFound();
break;
default:
throw new ArgumentOutOfRangeException(nameof(result));
}
}

public static Path<T> AsFound<T>(this Result<T> result) =>
Match(result, p => p, () => throw new InvalidOperationException("The result was not 'Found'"));
}

public record Found<T>(Path<T> Path) : Result<T>;

public record NotFound<T> : Result<T>;

public interface IAdjacency<T>
{
IEnumerable<(T newState, int weight)> GetAdjacent(T state);
}

public class AdhocAdjacency<T>(Func<T, IEnumerable<(T newState, int weight)>> function) : IAdjacency<T>
{
public IEnumerable<(T newState, int weight)> GetAdjacent(T pos) => function(pos);
}
32 changes: 32 additions & 0 deletions src/aoc/Common/Search/PathEnumerableExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace aoc.Common.Search;


public delegate bool Target<in T>(T state); // ITarget?

public static class Targets
{
public static Target<T> Value<T>(T value)
=> state => Equals(value, state);
}

public static class PathEnumerableExtensions
{
public static Path<T> FindTarget<T>(this IEnumerable<Path<T>> paths, Target<T> target)
{
return paths.First(path => target(path.HeadItem));
}

public static Result<T> TryFindTarget<T>(this IEnumerable<Path<T>> paths, Target<T> target)
{
foreach (var path in paths)
{
if (target(path.HeadItem))
return new Found<T>(path);
}

return new NotFound<T>();
}

public static IEnumerable<T> Items<T>(this IEnumerable<Path<T>> paths)
=> paths.Select(path => path.HeadItem);
}
Loading

0 comments on commit 30d27c3

Please sign in to comment.