diff --git a/Bobo.Systems.Maze.Console/Bot/BumpBot.cs b/Bobo.Systems.Maze.Console/Bot/BumpBot.cs new file mode 100644 index 0000000..baecac5 --- /dev/null +++ b/Bobo.Systems.Maze.Console/Bot/BumpBot.cs @@ -0,0 +1,47 @@ +using HightechICT.Amazeing.Client.Rest; +using LogConsole = System.Console; + +namespace Bobo.System.Maze.Console.Bot +{ + internal class BumpBot + { + internal static async Task Run(string mazeName, AmazeingClient mazeClient) + { + Random random = new Random(); + LogConsole.WriteLine($"Entering maze '{mazeName}'"); + PossibleActionsAndCurrentScore result = await mazeClient.EnterMaze(mazeName); + + do + { + await Task.Delay(100); + // select a way to go + MoveAction[] moves = result.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); + + LogConsole.WriteLine($"New tile has {(!result.CanExitMazeHere ? "no" : string.Empty)} exit."); + + } while (!result.CanExitMazeHere); + + await mazeClient.ExitMaze(); + + LogConsole.WriteLine($"Exited the maze!"); + } + } +} diff --git a/Bobo.Systems.Maze.Console/Bot/SimpleBot.cs b/Bobo.Systems.Maze.Console/Bot/SimpleBot.cs new file mode 100644 index 0000000..6c6301f --- /dev/null +++ b/Bobo.Systems.Maze.Console/Bot/SimpleBot.cs @@ -0,0 +1,169 @@ +using Bobo.Systems.Maze.Console.Model; +using HightechICT.Amazeing.Client.Rest; +using System.Collections.ObjectModel; +using LogConsole = System.Console; + + +namespace Bobo.System.Maze.Console.Bot +{ + internal class SimpleBot + { + private Collection Maze = new(); + + internal MazeTile CurrentTile { get; set; } = new(); + + internal 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; + } + } +} diff --git a/Bobo.Systems.Maze.Console/Bot/SimplerBot.cs b/Bobo.Systems.Maze.Console/Bot/SimplerBot.cs new file mode 100644 index 0000000..6cc44c5 --- /dev/null +++ b/Bobo.Systems.Maze.Console/Bot/SimplerBot.cs @@ -0,0 +1,85 @@ +using HightechICT.Amazeing.Client.Rest; +using LogConsole = System.Console; + + +namespace Bobo.System.Maze.Console.Bot +{ + internal class SimplerBot + { + internal static 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}'"); + PossibleActionsAndCurrentScore result = await mazeClient.EnterMaze(mazeName); + Stack directions = new Stack(); + int collected = 0; + + do + { + //await Task.Delay(500); + + if (result.PossibleMoveActions.All(m => m.HasBeenVisited)) + { + //LogConsole.WriteLine("All adjacent tiles have been visited! Going back."); + Direction direction = directions.Pop(); + + switch(direction) + { + case Direction.Left: + direction = Direction.Right; + break; + case Direction.Right: + direction = Direction.Left; + break; + case Direction.Up: + direction = Direction.Down; + break; + case Direction.Down: + direction = Direction.Up; + break; + + } + + //LogConsole.WriteLine($"Moving {direction}, I have been here."); + + result = await mazeClient.Move(direction); + } + else + { + MoveAction[] notVisited = result.PossibleMoveActions.Where(m => !m.HasBeenVisited).ToArray(); + //LogConsole.WriteLine($"{string.Join(", ", notVisited.Select(m => m.Direction.ToString()))} have not been visited, selecting next move."); + int selected = random.Next(notVisited.Length - 1); + MoveAction moveAction = notVisited[selected]; + + //LogConsole.WriteLine($"Moving {moveAction.Direction}, I have {(!moveAction.HasBeenVisited ? "not" : string.Empty)} been here."); + + result = await mazeClient.Move(moveAction.Direction); + + directions.Push(moveAction.Direction); + } + + if (result.CanCollectScoreHere && result.CurrentScoreInHand > 0) + { + collected += result.CurrentScoreInHand; + await mazeClient.CollectScore(); + } + + //LogConsole.WriteLine($"New tile has {(!result.CanExitMazeHere ? "no" : string.Empty)} exit."); + + } while (!result.CanExitMazeHere); + + await mazeClient.ExitMaze(); + + LogConsole.WriteLine($"Collected {collected}!"); + LogConsole.WriteLine($"Exited maze {mazeName}!"); + + return collected; + } + } +} diff --git a/Bobo.Systems.Maze.Console/Model/MazeTile.cs b/Bobo.Systems.Maze.Console/Model/MazeTile.cs new file mode 100644 index 0000000..db6172b --- /dev/null +++ b/Bobo.Systems.Maze.Console/Model/MazeTile.cs @@ -0,0 +1,38 @@ +using HightechICT.Amazeing.Client.Rest; + +namespace Bobo.Systems.Maze.Console.Model +{ + public class MazeTile : PossibleActionsAndCurrentScore + { + public int X { get; set; } = 0; + public int Y { get; set; } = 0; + public bool HasBeenVisited { get; set; } + + public MazeTile SetLocation(MazeTile source, Direction direction) + { + X = source.X; + Y = source.Y; + + switch (direction) + { + case Direction.Up: + X++; + break; + + case Direction.Down: + X--; + break; + + case Direction.Right: + Y++; + break; + + case Direction.Left: + Y--; + break; + } + + return this; + } + } +} diff --git a/Bobo.Systems.Maze.Console/Program.cs b/Bobo.Systems.Maze.Console/Program.cs index a93c787..2af2aed 100644 --- a/Bobo.Systems.Maze.Console/Program.cs +++ b/Bobo.Systems.Maze.Console/Program.cs @@ -16,14 +16,7 @@ httpClient.DefaultRequestHeaders.Add("Authorization", apiAuthorization); AmazeingClient mazeClient = new AmazeingClient(baseUrl, httpClient); - -ICollection mazeInfos = await mazeClient.AllMazes(); - -foreach(MazeInfo info in mazeInfos) -{ - Console.WriteLine($"Name: {info.Name}, Tiles: {info.TotalTiles}"); -} - +ICollection mazes = await mazeClient.AllMazes(); bool registered = await RegisterPlayer(username, true); @@ -35,9 +28,13 @@ if (!registered) Console.WriteLine($"Player '{username}' registered"); -Console.WriteLine($"Starting SimpleBot"); - -await SimpleBot.Run(mazeName, mazeClient); +Console.WriteLine($"Starting SimplerBot"); +foreach (MazeInfo maze in mazes) +{ + int collected = await SimplerBot.Run(maze.Name, mazeClient); + Console.WriteLine($"Collected {collected}/{maze.PotentialReward}"); + Console.WriteLine(); +} async Task RegisterPlayer(string username, bool reset = false) { @@ -65,7 +62,6 @@ async Task RegisterPlayer(string username, bool reset = false) { // forget the player. await mazeClient.ForgetPlayer(); - return true; } catch (ApiException apiEx) {