MazeRunner/Bobo.System.Maze.Bot/Bot.cs

169 lines
6.0 KiB
C#

using Bobo.Systems.Maze.Console.Model;
using HightechICT.Amazeing.Client.Rest;
using System.Collections.ObjectModel;
using LogConsole = System.Console;
namespace Bobo.System.Maze.Bot
{
public class Bot
{
private Collection<MazeTile> Maze = new();
internal MazeTile CurrentTile { get; set; } = new();
public async Task Run(string mazeName, AmazeingClient mazeClient)
{
if (mazeClient == null)
throw new ArgumentException(nameof(mazeClient));
if (string.IsNullOrWhiteSpace(mazeName))
throw new ArgumentException(nameof(mazeName));
Random random = new Random();
LogConsole.WriteLine($"Entering maze '{mazeName}'");
MazeTile result = new();
CurrentTile = (await mazeClient.EnterMaze(mazeName)).ToMazeTile();
Maze.Add(CurrentTile);
foreach (MazeTile item in CurrentTile.PossibleMoveActions.Select(move => move.ToMazeTile(CurrentTile)))
{
AddOrUpdateMaze(item);
}
RenderMaze();
do
{
await Task.Delay(100);
LogConsole.Clear();
// select a way to go
MoveAction[] moves = CurrentTile.PossibleMoveActions.ToArray();
MoveAction[] notExploredMoves = moves.Any(m => !m.HasBeenVisited) ? moves.Where(m => !m.HasBeenVisited).ToArray() : moves;
if (moves.All(m => m.HasBeenVisited))
{
LogConsole.WriteLine("All adjacent tiles have been visited! Selecting next path random.");
}
else
{
LogConsole.WriteLine($"{string.Join(", ", notExploredMoves.Select(m => m.Direction.ToString()))} have not been visited, selecting next move.");
}
int selected = random.Next(notExploredMoves.Length - 1);
MoveAction moveAction = notExploredMoves[selected];
LogConsole.WriteLine($"Moving {moveAction.Direction}, I have {(!moveAction.HasBeenVisited ? "not" : string.Empty)} been here.");
result = (await mazeClient.Move(moveAction.Direction))
.ToMazeTile()
.SetLocation(CurrentTile, moveAction.Direction);
CurrentTile = result;
AddOrUpdateMaze(result);
foreach (MazeTile item in result.PossibleMoveActions.Select(move => move.ToMazeTile(result)))
{
AddOrUpdateMaze(item);
}
LogConsole.WriteLine($"New tile has {(!CurrentTile.CanExitMazeHere ? "no" : string.Empty)} exit.");
RenderMaze();
LogConsole.ReadKey(true);
} while (!CurrentTile.CanExitMazeHere);
await mazeClient.ExitMaze();
LogConsole.WriteLine($"Exited the maze!");
}
internal void AddOrUpdateMaze(MazeTile tile)
{
string tileLocation = $"[{tile.X},{tile.Y}]";
// fetch the tile from the collection, if there is one
MazeTile? inCollection = Maze.SingleOrDefault(listTile => listTile.X == tile.X && listTile.Y == tile.Y);
if (inCollection != null)
{
if (CurrentTile.X == inCollection.X && CurrentTile.Y == tile.Y)
{
LogConsole.WriteLine($"{tileLocation} updating tile standing tile");
Maze.Remove(inCollection);
}
else
{
LogConsole.WriteLine($"{tileLocation} tile exists and we are not on it, skiping tile.");
return;
}
}
else
{
LogConsole.WriteLine($"{tileLocation} not found in collection.");
}
LogConsole.WriteLine($"Adding {tileLocation} to Maze collecion");
Maze.Add(tile);
}
internal void RenderMaze()
{
int minY = Maze.Min(t => t.Y), maxY = Maze.Max(t => t.Y);
int totalY = Math.Abs(maxY - minY);
// render the header
// render the left prefix
LogConsole.Write(" " + string.Join("", Enumerable.Range(minY, totalY).Select(t => PadBoth(t.ToString(), 3))));
LogConsole.WriteLine();
// render the row of X tiles + the paths between if any
foreach (MazeTile tile in Maze.OrderBy(t => t.X).ThenBy(t => t.Y))
{
LogConsole.WriteLine($"[{tile.X},{tile.Y}] - visited: {tile.HasBeenVisited}; paths: {(tile.PossibleMoveActions != null ? string.Join(',', tile.PossibleMoveActions.Select(d => d.Direction.ToString())) : "unknown")}");
}
}
internal string PadBoth(string source, int length)
{
int spaces = length - source.Length;
int padLeft = spaces / 2 + source.Length;
return source.PadLeft(padLeft).PadRight(length);
}
}
public static class Converter
{
public static MazeTile ToMazeTile(this PossibleActionsAndCurrentScore possibleActionsAndCurrentScore)
{
MazeTile tile = new();
tile.PossibleMoveActions = possibleActionsAndCurrentScore.PossibleMoveActions;
tile.CanCollectScoreHere = possibleActionsAndCurrentScore.CanCollectScoreHere;
tile.CanExitMazeHere = possibleActionsAndCurrentScore.CanExitMazeHere;
tile.CurrentScoreInHand = possibleActionsAndCurrentScore.CurrentScoreInHand;
tile.CurrentScoreInBag = possibleActionsAndCurrentScore.CurrentScoreInBag;
tile.HasBeenVisited = true;
return tile;
}
public static MazeTile ToMazeTile(this MoveAction moveAction, MazeTile source)
{
MazeTile tile = new MazeTile();
tile.HasBeenVisited = moveAction.HasBeenVisited;
tile.SetLocation(source, moveAction.Direction);
return tile;
}
}
}