Skip to content

Commit

Permalink
Optimized choice making in wide open spaces
Browse files Browse the repository at this point in the history
  • Loading branch information
boekabart committed Feb 25, 2020
1 parent d7b4a0f commit 7b7d62e
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 40 deletions.
34 changes: 32 additions & 2 deletions Extensions.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using System.Collections;
using System.Collections.Generic;

namespace Maze
{
internal static class Extensions
{
public static Direction Reversed( this Direction bestDir)
public static Direction Reversed(this Direction bestDir)
{
switch (bestDir)
{
Expand All @@ -16,5 +18,33 @@ public static Direction Reversed( this Direction bestDir)
throw new ArgumentException("Bad dir");
}
}

internal static (int X, int Y) Moved(this (int X, int Y) location, Direction dir)
{
switch (dir)
{
case Direction.Up:
return (location.X, location.Y + 1);
case Direction.Right:
return (location.X + 1, location.Y);
case Direction.Down:
return (location.X, location.Y - 1);
case Direction.Left:
return (location.X - 1, location.Y);
default:
throw new ArgumentOutOfRangeException(nameof(dir), dir, null);
}
}

internal static IEnumerable<Direction> AllDirections
{
get
{
yield return Direction.Up;
yield return Direction.Down;
yield return Direction.Left;
yield return Direction.Right;
}
}
}
}
}
7 changes: 7 additions & 0 deletions Global.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Maze
{
internal static class Global
{
public static bool IsInteractive { get; set; }
}
}
21 changes: 15 additions & 6 deletions MazeSolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ private async Task GoToExit(PossibleActionsAndCurrentScore options)
// Looking for exit!
if (options.CanExitMazeHere)
{
//Console.WriteLine("Enter please");
//Console.ReadLine();
if (Global.IsInteractive)
{
_xyGrid.Draw("Finished");
Console.ReadKey();
}
await _client.ExitMaze();

return;
}

Expand Down Expand Up @@ -134,10 +138,10 @@ private async Task<PossibleActionsAndCurrentScore> CollectAllPoints(PossibleActi
Console.Error.WriteLine("Stuck while collecting!");
}

if (false && !_xyGrid.InvalidState)
if (!_xyGrid.InvalidState)
{
_xyGrid.Draw();
Console.ReadKey();
//_xyGrid.Draw();
//Console.ReadKey();
}

return options;
Expand Down Expand Up @@ -196,6 +200,9 @@ private async Task<PossibleActionsAndCurrentScore> MakeMove(Direction direction,
// Check for nearby exits and collectionPoints
TrackExits(newOptions, _exitCrumbs);
TrackCollectionPoints(newOptions, _collectCrumbs);

//_xyGrid.Draw();
//Console.ReadKey(true);

return newOptions;
}
Expand All @@ -222,7 +229,9 @@ private MoveAction MostUsefulDirForCollecting(PossibleActionsAndCurrentScore opt
//.ThenBy(ma => ma.AllowsScoreCollection) // Un-prefer ScoreCollection Points, we'll get there later
//.ThenBy(ma => ma.AllowsExit) // Un-prefer exits, we'll get there later
.ThenBy(ma => ma.RewardOnDestination == 0) // Prefer reward directions over non-reward. It might be the last straw!
.ThenBy(ma => _xyGrid.SeenTile(ma.Direction)) // Un-prefer tiles I've seen and apparently didn't visit.. for a reason?
//.ThenBy(ma => _xyGrid.SeenTile(ma.Direction)) // Un-prefer tiles I've seen and apparently didn't visit.. for a reason?
.ThenByDescending(ma => _xyGrid.HasIslandNeighbor(ma.Direction)) // prefer tiles that will lead to completion of an unknown island
.ThenByDescending(ma => _xyGrid.UnvisitedPotential(ma.Direction))
.ThenBy(ma => HugTheLeftWall(ma.Direction, crawlCrumbs))
//.ThenBy(ma => RandomGenerator.Next())
.ThenByDescending(ma => ma.Direction) // Prefer Starting Left over Down over Right over Up... no real reason, just for predictability
Expand Down
4 changes: 3 additions & 1 deletion Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ static async Task Main(string[] args)
}

var mazeNames= args.Skip(3).Select(name => name.ToLowerInvariant()).ToHashSet();
Global.IsInteractive = mazeNames.Any();

var serverHost = args.Skip(2).FirstOrDefault() ?? "maze.hightechict.nl";
var apiKey = args.FirstOrDefault() ?? throw new Exception("Key?");
var ourName = args.Skip(1).FirstOrDefault() ?? "deBoerIsTroef";
Expand Down Expand Up @@ -51,7 +53,7 @@ static async Task Main(string[] args)
}

// Do the Konami Move
if (!mazeNames.Any())
if (!Global.IsInteractive)
{
var baseInvocations = client.Invocations;
await DoTheKonami(client);
Expand Down
76 changes: 45 additions & 31 deletions XyGrid.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Drawing;
using System.Linq;

namespace Maze
Expand Down Expand Up @@ -37,7 +36,7 @@ public static Tile TryMerge(MoveAction moveAction, Tile previousKnownState)
{
if (moveAction.AllowsExit != previousKnownState.IsExit
|| moveAction.AllowsScoreCollection != previousKnownState.IsCollectionPoint
|| moveAction.HasBeenVisited != previousKnownState.IsVisited)
/*|| moveAction.HasBeenVisited != previousKnownState.IsVisited*/) // HEY, loop detection
return null;
return new Tile(moveAction, previousKnownState.PossibleDirections);
}
Expand Down Expand Up @@ -66,11 +65,12 @@ public static bool Match(ISet<Direction> lhs, ISet<Direction> rhs)
}
}

public void Draw()
public void Draw(string status)
{
var offsetX = _dick.Keys.Min(xy => xy.X);
var offsetY = _dick.Keys.Max(xy => xy.Y);
Console.Clear();
Console.Error.WriteLine(status);
foreach (var kvp in _dick)
{
var x = 1 + kvp.Key.X - offsetX;
Expand All @@ -79,18 +79,15 @@ public void Draw()
Console.SetCursorPosition(x, y);
var tile = kvp.Value;
Console.ForegroundColor = tile.IsVisited ? ConsoleColor.White : ConsoleColor.DarkGray;
Console.Write( "X");
Console.Error.Write( $"{(HasIslandNeighbor(kvp.Key)?"!":UnvisitedPotential(kvp.Key).ToString())}");
}
Console.SetCursorPosition(1 + X - offsetX, 1 + offsetY - Y);
Console.SetCursorPosition(1 + CurrentLocation.X - offsetX, 1 + offsetY - CurrentLocation.Y);
}

public (int X, int Y) CurrentLocation { get; private set; }
public int X => CurrentLocation.X;
public int Y => CurrentLocation.Y;
public bool InvalidState { get; private set; }
public void Register(PossibleActionsAndCurrentScore options)
{
if (InvalidState) return;
if (!UpdateCurrent(options))
return;

Expand All @@ -103,6 +100,27 @@ public void Register(PossibleActionsAndCurrentScore options)

public int SeenTile(Direction dir) => InvalidState ? 0 :
_dick.TryGetValue(Moved(dir), out var tile) ? tile.PossibleDirections.Count : 0;

public int UnvisitedPotential(Direction dir) => UnvisitedPotential(Moved(dir));

private int UnvisitedPotential((int X, int Y) pos) => InvalidState?0:Extensions.AllDirections.Select(d => pos.Moved(d)).Max(HowManyUnknownNeighbours);

private int HowManyUnknownNeighbours((int X, int Y) pos)
{
return InvalidState ? 0 : Extensions.AllDirections
.Select(d => pos.Moved(d))
.Count(p => !_dick.ContainsKey(p));
}

public bool HasIslandNeighbor(Direction dir) => HasIslandNeighbor(Moved(dir));

private bool HasIslandNeighbor((int X, int Y) pos)
{
return !InvalidState && Extensions.AllDirections
.Select(d => pos.Moved(d))
.Where(p => !_dick.ContainsKey(p)) // Unknown location
.Any(p => HowManyUnknownNeighbours(p) == 0); // Without any unknown neighbours
}

private readonly Dictionary<(int X, int Y), Tile> _dick = new Dictionary<(int X, int Y), Tile>();

Expand All @@ -114,10 +132,15 @@ private bool UpdateCurrent(PossibleActionsAndCurrentScore options)

if (newTile == null)
{
// Draw();
// Console.ReadKey();
if (InvalidState) return false;
var message = $"XY-incompatible maze detected. Conflict with known state at {CurrentLocation.X},{CurrentLocation.Y}.";
if (Global.IsInteractive)
{
Draw(message);
Console.ReadKey();
}

Console.Error.WriteLine($"XY-incompatible maze detected. Conflict with known state at {CurrentLocation.X},{CurrentLocation.Y}.");
Console.Error.WriteLine(message);
InvalidState = true;
return false;
}
Expand All @@ -132,15 +155,21 @@ private bool UpdateNext(MoveAction moveAction)
var newTile = _dick.TryGetValue(location, out Tile previousState)
? Tile.TryMerge(moveAction, previousState)
: moveAction.HasBeenVisited
? null
? new Tile(moveAction) // HEY! That was unexpected!? Portal detected!!
: new Tile(moveAction);

if (newTile == null)
{
// Draw();
// Console.ReadKey();
if (InvalidState) return false;
var message =
$"XY-incompatible maze detected. Conflict with known state at {location.X},{location.Y} {moveAction.Direction}.";
if (Global.IsInteractive)
{
Draw(message);
Console.ReadKey();
}

Console.Error.WriteLine($"XY-incompatible maze detected. Conflict with known state at {location.X},{location.Y} {moveAction.Direction}.");
Console.Error.WriteLine(message);
InvalidState = true;
return false;
}
Expand All @@ -149,22 +178,7 @@ private bool UpdateNext(MoveAction moveAction)
return true;
}

private (int X, int Y) Moved(Direction dir)
{
switch(dir)
{
case Direction.Up:
return (X, Y + 1);
case Direction.Right:
return (X + 1, Y);
case Direction.Down:
return (X, Y - 1);
case Direction.Left:
return (X - 1, Y);
default:
throw new ArgumentOutOfRangeException(nameof(dir), dir, null);
}
}
private (int X, int Y) Moved(Direction dir) => CurrentLocation.Moved(dir);

public void RegisterMove(Direction dir) => CurrentLocation = Moved(dir);
}
Expand Down

0 comments on commit 7b7d62e

Please sign in to comment.