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 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; } } }