diff --git a/Projects/Hangman/README.md b/Projects/Hangman/README.md index 2cdd272b..1dd7d78a 100644 --- a/Projects/Hangman/README.md +++ b/Projects/Hangman/README.md @@ -6,8 +6,6 @@ Hangman is a game that chooses a random word from the english language, and you try to guess the word. If you make too many incorrect guesses you lose. -**_This game is depended on a "Words.txt" embedded resource file to provide the pool of possible random words. If you copy the code without the embedded resource it will not work._** - ``` ╔═══╗ @@ -22,4 +20,10 @@ Hangman is a game that chooses a random word from the english language, and you ## Input -The **alphabetic keys (a, b, c, ...)** are used to guess letters. The **escape** key may be used to close the game at any time. If you **resize** the console widow the game will be closed. \ No newline at end of file +The **alphabetic keys (a, b, c, ...)** are used to guess letters. The **escape** key may be used to close the game at any time. If you **resize** the console widow the game will be closed. + +## Dependencies + +Don't forget these dependencies if you copy the code: + +- "Words.txt" embedded resource _(included in source)_ diff --git a/Projects/Maze/Prims/Graph.cs b/Projects/Maze/Prims/Graph.cs index 259e4842..324b3fb9 100644 --- a/Projects/Maze/Prims/Graph.cs +++ b/Projects/Maze/Prims/Graph.cs @@ -1,34 +1,35 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -public class Graph -{ - public class Node - { - public int OwnIndex { get; } - public List Connections { get; } - public List Costs { get; } +namespace Prims +{ + public class Graph + { + public class Node + { + public int OwnIndex { get; } + public List Connections { get; } + public List Costs { get; } - public void Add(int other, double cost) - { - Connections.Add(other); - Costs.Add(cost); - } + public void Add(int other, double cost) + { + Connections.Add(other); + Costs.Add(cost); + } - public Node(int ownIndex) - { - OwnIndex = ownIndex; - Connections = new List(); - Costs = new List(); - } - } + public Node(int ownIndex) + { + OwnIndex = ownIndex; + Connections = new List(); + Costs = new List(); + } + } - public Node[] Nodes { get; } + public Node[] Nodes { get; } - public Graph(Node[] nodes) - { - Nodes = nodes ?? throw new ArgumentNullException(nameof(nodes)); - } + public Graph(Node[] nodes) + { + Nodes = nodes ?? throw new ArgumentNullException(nameof(nodes)); + } + } } diff --git a/Projects/Maze/Prims/PrimsAlgorithm.cs b/Projects/Maze/Prims/PrimsAlgorithm.cs index a519a9b0..42b2609c 100644 --- a/Projects/Maze/Prims/PrimsAlgorithm.cs +++ b/Projects/Maze/Prims/PrimsAlgorithm.cs @@ -1,58 +1,59 @@ - -using System; +using System; using Towel.DataStructures; -public static class PrimsAlgorithm +namespace Prims { - private readonly struct TwoWayConnection : IComparable - { - public readonly int IndexA; - public readonly int IndexB; - public readonly double Cost; - - public TwoWayConnection(int indexA, int indexB, double cost) - { - IndexA = indexA; - IndexB = indexB; - Cost = cost; - } - - public int CompareTo(TwoWayConnection other) => other.Cost.CompareTo(Cost); // inversed because of how the heap works - } - - public static Graph SimplePrims(Graph graph) - { - var newGraph = new Graph(new Graph.Node[graph.Nodes.Length]); - var nodes = graph.Nodes; - var current = nodes[0]; - newGraph.Nodes[0] = new Graph.Node(0); - - var heap = new HeapArray(); - - - while(true) - { - for (int i = 0; i < current.Connections.Count; i++) - { - heap.Enqueue(new TwoWayConnection(current.OwnIndex, current.Connections[i], current.Costs[i])); - } - - TwoWayConnection c; - do - { - if (heap.Count == 0) - return newGraph; - - c = heap.Dequeue(); - } - while (newGraph.Nodes[c.IndexB] != null); - - - newGraph.Nodes[c.IndexA].Add(c.IndexB, c.Cost); - - newGraph.Nodes[c.IndexB] = new Graph.Node(c.IndexB); - current = graph.Nodes[c.IndexB]; - newGraph.Nodes[c.IndexB].Add(c.IndexA, c.Cost); - } - } + public static class PrimsAlgorithm + { + private readonly struct TwoWayConnection : IComparable + { + public readonly int IndexA; + public readonly int IndexB; + public readonly double Cost; + + public TwoWayConnection(int indexA, int indexB, double cost) + { + IndexA = indexA; + IndexB = indexB; + Cost = cost; + } + + public int CompareTo(TwoWayConnection other) => other.Cost.CompareTo(Cost); // inversed because of how the heap works + } + + public static Graph SimplePrims(Graph graph) + { + var newGraph = new Graph(new Graph.Node[graph.Nodes.Length]); + var nodes = graph.Nodes; + var current = nodes[0]; + newGraph.Nodes[0] = new Graph.Node(0); + + var heap = new HeapArray(); + + while (true) + { + for (int i = 0; i < current.Connections.Count; i++) + { + heap.Enqueue(new TwoWayConnection(current.OwnIndex, current.Connections[i], current.Costs[i])); + } + + TwoWayConnection c; + do + { + if (heap.Count == 0) + { + return newGraph; + } + c = heap.Dequeue(); + } + while (newGraph.Nodes[c.IndexB] != null); + + newGraph.Nodes[c.IndexA].Add(c.IndexB, c.Cost); + + newGraph.Nodes[c.IndexB] = new Graph.Node(c.IndexB); + current = graph.Nodes[c.IndexB]; + newGraph.Nodes[c.IndexB].Add(c.IndexA, c.Cost); + } + } + } } \ No newline at end of file diff --git a/Projects/Maze/Prims/PrimsMazeGenerator.cs b/Projects/Maze/Prims/PrimsMazeGenerator.cs index aaeba61b..3db0c1fd 100644 --- a/Projects/Maze/Prims/PrimsMazeGenerator.cs +++ b/Projects/Maze/Prims/PrimsMazeGenerator.cs @@ -2,84 +2,92 @@ namespace Prims { - public class PrimsMazeGenerator - { - public static Maze.Tile[,] Generate( - int rows, int columns, - int? start_row = null, int? start_column = null, - int? end_row = null, int? end_column = null) - { - start_row ??= 0; - start_column ??= 0; - end_row ??= rows - 1; - end_column ??= columns - 1; - - var grid = new Graph.Node[rows * columns]; - int Index(int row, int col) => row + rows * col; - (int, int) Unpack(int i) => (i % rows, i / rows); - - var random = new Random(); - - for (int row = 0; row < rows; row++) - { - for (int col = 0; col < columns; col++) - { - var n = new Graph.Node(Index(row, col)); - - if (row + 1 < rows) - n.Add(Index(row + 1, col), random.NextDouble()); - - if (row - 1 >= 0) - n.Add(Index(row - 1, col), random.NextDouble()); - - if (col + 1 < columns) - n.Add(Index(row, col + 1), random.NextDouble()); - - if (col - 1 >= 0) - n.Add(Index(row, col - 1), random.NextDouble()); - - grid[Index(row, col)] = n; - } - } - - var res = PrimsAlgorithm.SimplePrims(new Graph(grid)); - - var tiles = new Maze.Tile[rows,columns]; - - foreach (var node in res.Nodes) - { - var (row, col) = Unpack(node.OwnIndex); - - if (node.Connections.Contains(Index(row - 1, col))) - { - tiles[row, col] |= Maze.Tile.Up; - tiles[row - 1, col] |= Maze.Tile.Down; - } - - if (node.Connections.Contains(Index(row + 1, col))) - { - tiles[row, col] |= Maze.Tile.Down; - tiles[row + 1, col] |= Maze.Tile.Up; - } - - if (node.Connections.Contains(Index(row, col - 1))) - { - tiles[row, col] |= Maze.Tile.Left; - tiles[row, col - 1] |= Maze.Tile.Right; - } - - if (node.Connections.Contains(Index(row, col + 1))) - { - tiles[row, col] |= Maze.Tile.Right; - tiles[row, col + 1] |= Maze.Tile.Left; - } - - if (row == start_row.Value && col == start_column.Value) tiles[row, col] |= Maze.Tile.Start; - if (row == end_row.Value && col == end_column.Value) tiles[row, col] |= Maze.Tile.End; - - } - - return tiles; - } - } + public class PrimsMazeGenerator + { + public static Maze.Tile[,] Generate( + int rows, int columns, + int? start_row = null, int? start_column = null, + int? end_row = null, int? end_column = null) + { + start_row ??= 0; + start_column ??= 0; + end_row ??= rows - 1; + end_column ??= columns - 1; + + var grid = new Graph.Node[rows * columns]; + + int Index(int row, int col) => row + rows * col; + + var random = new Random(); + + for (int row = 0; row < rows; row++) + { + for (int col = 0; col < columns; col++) + { + var n = new Graph.Node(Index(row, col)); + if (row + 1 < rows) + { + n.Add(Index(row + 1, col), random.NextDouble()); + } + if (row - 1 >= 0) + { + n.Add(Index(row - 1, col), random.NextDouble()); + } + if (col + 1 < columns) + { + n.Add(Index(row, col + 1), random.NextDouble()); + } + if (col - 1 >= 0) + { + n.Add(Index(row, col - 1), random.NextDouble()); + } + grid[Index(row, col)] = n; + } + } + + var res = PrimsAlgorithm.SimplePrims(new Graph(grid)); + + var tiles = new Maze.Tile[rows, columns]; + + foreach (var node in res.Nodes) + { + (int, int) Unpack(int i) => (i % rows, i / rows); + + var (row, col) = Unpack(node.OwnIndex); + + // directional + if (node.Connections.Contains(Index(row - 1, col))) + { + tiles[row, col] |= Maze.Tile.Up; + tiles[row - 1, col] |= Maze.Tile.Down; + } + if (node.Connections.Contains(Index(row + 1, col))) + { + tiles[row, col] |= Maze.Tile.Down; + tiles[row + 1, col] |= Maze.Tile.Up; + } + if (node.Connections.Contains(Index(row, col - 1))) + { + tiles[row, col] |= Maze.Tile.Left; + tiles[row, col - 1] |= Maze.Tile.Right; + } + if (node.Connections.Contains(Index(row, col + 1))) + { + tiles[row, col] |= Maze.Tile.Right; + tiles[row, col + 1] |= Maze.Tile.Left; + } + + // start/end + if (row == start_row.Value && col == start_column.Value) + { + tiles[row, col] |= Maze.Tile.Start; + } + if (row == end_row.Value && col == end_column.Value) + { + tiles[row, col] |= Maze.Tile.End; + } + } + return tiles; + } + } } \ No newline at end of file diff --git a/Projects/Maze/Program.cs b/Projects/Maze/Program.cs index e05a220c..efe49711 100644 --- a/Projects/Maze/Program.cs +++ b/Projects/Maze/Program.cs @@ -1,11 +1,13 @@ //#define MazeGenertorLoop // uncomment to run the generator in a loop //#define DebugRandomMazeGeneration // uncomment me to watch the maze being built node-by-node -//#define UsePrims +//#define UsePrims // uncomment me to use an alternate algorithm for maze generation using System; using System.Collections.Generic; using System.Text; +#if UsePrims using Prims; +#endif class Program { @@ -19,7 +21,7 @@ static void Main() PrimsMazeGenerator.Generate(rows, columns); #else Maze.Generate(rows, columns); - #endif +#endif #if MazeGenertorLoop while (true) { diff --git a/Projects/Maze/README.md b/Projects/Maze/README.md index e64074fa..32b55485 100644 --- a/Projects/Maze/README.md +++ b/Projects/Maze/README.md @@ -2,7 +2,7 @@ ![](https://github.com/ZacharyPatten/dotnet-console-games/workflows/Maze%20Build/badge.svg) -**[Source Code](Program.cs)** +**Source Code**: see files above Maze is a pretty self explanatory game. Solve the randomly generated maze. The maze size if coded to be 8x20. You always start in the top left and the end is always in the bottom right. @@ -41,4 +41,10 @@ The **arrow keys (↑, ↓, ←, →)** are used to change the direction you are At the top of the **[source code](Program.cs)** you will see two compiler directives... - `#define MazeGenertorLoop`: Uncomment this directive and you can watch the code generate mazes infinitely inside a `while (true)` loop. -- `#define DebugRandomMazeGeneration`: Uncomment this directive and you can watch the maze generation algorithm step-by-step. \ No newline at end of file +- `#define DebugRandomMazeGeneration`: Uncomment this directive and you can watch the maze generation algorithm step-by-step. + +## Dependencies + +Don't forget these dependencies if you copy the code: + +- [Towel](https://github.com/ZacharyPatten/Towel) nuget package _(referenced in [Maze.csproj](Maze.csproj))_ diff --git a/Projects/Wumpus World/Program.cs b/Projects/Wumpus World/Program.cs index 161b88a7..3268dc62 100644 --- a/Projects/Wumpus World/Program.cs +++ b/Projects/Wumpus World/Program.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using static System.Console; +using Towel; class Program { @@ -188,7 +189,7 @@ static void GenerateCave() Map = new Tile[width, height]; // Get Random Locations Random random = new Random(); - (int X, int Y)[] randomCoordinates = random.UniqueInts(5, 1..(width * height)).Select(i => (i / width, i % width)).ToArray(); + (int X, int Y)[] randomCoordinates = random.NextUnique(5, 1, (width * height)).Select(i => (i / width, i % width)).ToArray(); var wumpusLocation = randomCoordinates[0]; var goldLocation = randomCoordinates[1]; var pitLocations = randomCoordinates[2..^0]; @@ -217,46 +218,3 @@ enum Tile Gold, Pit, } - -public static class Helpers -{ - internal class Node - { - internal int Value; - internal Node Next; - } - - public static IEnumerable UniqueInts(this Random random, int count, Range range) - { - if (random is null) - throw new ArgumentNullException(nameof(random)); - (int a, int b) = range.End.Value < range.Start.Value - ? (range.End.Value, range.Start.Value) - : (range.Start.Value, range.End.Value); - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count) + " < 0"); - if (b - a < count) - throw new ArgumentOutOfRangeException(nameof(count) + " is larger than " + nameof(range)); - - Node head = null; - for (int i = 0; i < count; i++) - { - int roll = random.Next(a, b - i); - Node node = head; - Node previous = null; - while (!(node is null)) - { - if (node.Value > roll) - break; - roll++; - previous = node; - node = node.Next; - } - yield return roll; - if (previous is null) - head = new Node() { Value = roll, Next = head, }; - else - previous.Next = new Node() { Value = roll, Next = previous.Next }; - } - } -} diff --git a/Projects/Wumpus World/README.md b/Projects/Wumpus World/README.md index 53da0529..7955010d 100644 --- a/Projects/Wumpus World/README.md +++ b/Projects/Wumpus World/README.md @@ -35,3 +35,9 @@ This game uses commands. Each screen will give you the list of available command - `right` - `yes` - `no` + +## Dependencies + +Don't forget these dependencies if you copy the code: + +- [Towel](https://github.com/ZacharyPatten/Towel) nuget package _(referenced in [Wumpus World.csproj](Wumpus World.csproj))_ diff --git a/Projects/Wumpus World/Wumpus World.csproj b/Projects/Wumpus World/Wumpus World.csproj index 5a8d0cbe..3d7565e7 100644 --- a/Projects/Wumpus World/Wumpus World.csproj +++ b/Projects/Wumpus World/Wumpus World.csproj @@ -1,4 +1,4 @@ - + Exe @@ -6,4 +6,8 @@ Wumpus_World + + + + diff --git a/README.md b/README.md index 59da1459..547c74c6 100644 --- a/README.md +++ b/README.md @@ -4,20 +4,23 @@ This repository is a collection of game examples implemented as .NET Core consol |Game|\*Complexity|Notes| |:-|:-|:-| -|[Tic Tac Toe](https://github.com/ZacharyPatten/dotnet-console-games/tree/master/Projects/Tic%20Tac%20Toe)|1|Discord
`Random`, `char[,]`| -|[Simon](https://github.com/ZacharyPatten/dotnet-console-games/tree/master/Projects/Simon)|1|Build
`List`, `Random`, `TimeSpan`, `enum`| -|[Beep Pad](https://github.com/ZacharyPatten/dotnet-console-games/tree/master/Projects/Beep%20Pad)|1|Build
`Random`, `Queue`, `ValueTuple<...>`| -|[Hangman](https://github.com/ZacharyPatten/dotnet-console-games/tree/master/Projects/Hangman)|2|Build
`Random`, `Assembly`, `T[]`, `List`, `StreamReader`| -|[Minesweeper](https://github.com/ZacharyPatten/dotnet-console-games/tree/master/Projects/Minesweeper)|2|Build
`Random (multiple unique)`, `(recursion)`, `ValueTuple<...>`, `IEnumerable`| -|[Wumpus World](https://github.com/ZacharyPatten/dotnet-console-games/tree/master/Projects/Wumpus%20World)|2|Build
`Random (multiple unique)`, `enum`, `enum[,]`, `ValueTuple<...>`| -|[Snake](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Snake)|3|Build
`enum`, `TimeSpan`, `Queue`, `ValueTuple<...>`| -|[Hurdles](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Hurdles)|3|Build
`TimeSpan`, `string[]`, `Nullable`| -|[Pong](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Pong)|3|Build
`Random`, `TimeSpan`, `ValueTuple<...>`, `class`, `Stopwatch`| -|[Flappy Bird](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Flappy%20Bird)|3|Build
`Random`, `TimeSpan`, `ValueTuple<...>`, `List`| -|[Tanks](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Tanks)|4|Build
`Random`, `TimeSpan`, `class`, `enum`, `string[]`| -|[Helicopter](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Helicopter)|4|Build
`Random`, `TimeSpan`, `class`, `List`, `string[]`| -|[Fighter](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Fighter)|5|Build
`Random`, `TimeSpan`, `enum`, `string[]`| -|[Maze](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Maze)|5|Build
`Random (path finding)`, `class`, `[Flags] enum`, `enum[,]`, `Stack`, `StringBuilder`, `ValueTuple<...>`, `Nullable`| +|[Tic Tac Toe](https://github.com/ZacharyPatten/dotnet-console-games/tree/master/Projects/Tic%20Tac%20Toe)|1|Discord| +|[Simon](https://github.com/ZacharyPatten/dotnet-console-games/tree/master/Projects/Simon)|1|Build| +|[Beep Pad](https://github.com/ZacharyPatten/dotnet-console-games/tree/master/Projects/Beep%20Pad)|1|Build| +|[Hangman](https://github.com/ZacharyPatten/dotnet-console-games/tree/master/Projects/Hangman)|2|Build| +|[Minesweeper](https://github.com/ZacharyPatten/dotnet-console-games/tree/master/Projects/Minesweeper)|2|Build| +|[Wumpus World](https://github.com/ZacharyPatten/dotnet-console-games/tree/master/Projects/Wumpus%20World)|2|Build| +|[Snake](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Snake)|3|Build| +|[Hurdles](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Hurdles)|3|Build| +|[Pong](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Pong)|3|Build| +|[Flappy Bird](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Flappy%20Bird)|3|Build| +|[Tanks](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Tanks)|4|Build| +|[Helicopter](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Helicopter)|4|Build| +|[Fighter](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Fighter)|5|Build| +|[Maze](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/Maze)|5|Build| +|[PacMan](https://github.com/ZacharyPatten/dotnet-console-games/blob/master/Projects/PacMan)|N/A|**_still in development_**: The game is functional and shows off movement and scoring, but the AI for the ghost enemies is still in development. | +|Hearts|N/A|**_still in development_**| +|Tetris|N/A|**_still in development_**| _\***Complexity**: a relative rating for how complex/advanced the source code is._