diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index a6a59da..340cd7a 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -1,9 +1,9 @@ name: Build -on: [push, pull_request] +on: [push] jobs: - build: + build-Windows: runs-on: windows-latest @@ -20,4 +20,21 @@ jobs: - name: Test run: $slnList = Get-ChildItem $foo.FullName -Recurse -Filter '*.sln'; foreach ($file in $slnList) {dotnet test $file.FullName} + build-Ubuntu: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Build + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '6.x' + - name: Restore + run: for f in $(find . -name "*.sln"); do dotnet restore $f; done + - name: Build + run: for f in $(find . -name "*.sln"); do dotnet build $f; done + - name: Test + run: for f in $(find . -name "*.sln"); do dotnet test $f; done + diff --git a/Game/Game/Game.sln b/Game/Game/Game.sln new file mode 100644 index 0000000..1b1480d --- /dev/null +++ b/Game/Game/Game.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32228.430 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Game", "Game\Game.csproj", "{5C94A5A6-4A89-4028-87EB-872F3053657A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameTest", "GameTest\GameTest.csproj", "{194D762F-14D0-4A7D-993D-FB324AAC0E1D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5C94A5A6-4A89-4028-87EB-872F3053657A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C94A5A6-4A89-4028-87EB-872F3053657A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C94A5A6-4A89-4028-87EB-872F3053657A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C94A5A6-4A89-4028-87EB-872F3053657A}.Release|Any CPU.Build.0 = Release|Any CPU + {194D762F-14D0-4A7D-993D-FB324AAC0E1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {194D762F-14D0-4A7D-993D-FB324AAC0E1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {194D762F-14D0-4A7D-993D-FB324AAC0E1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {194D762F-14D0-4A7D-993D-FB324AAC0E1D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C9167AF3-096A-436F-9070-46CD6A12D020} + EndGlobalSection +EndGlobal diff --git a/Game/Game/Game/EventLoop.cs b/Game/Game/Game/EventLoop.cs new file mode 100644 index 0000000..77e06e0 --- /dev/null +++ b/Game/Game/Game/EventLoop.cs @@ -0,0 +1,69 @@ +namespace Game; + +using System; + +/// +/// A class representing an event handler loop +/// +public class EventLoop +{ + /// + /// Handler for the left move event + /// + public event EventHandler LeftHandler = (sender, args) => { }; + + /// + /// Handler for the right move event + /// + public event EventHandler RightHandler = (sender, args) => { }; + + /// + /// Handler for the up move event + /// + public event EventHandler UpHandler = (sender, args) => { }; + + /// + /// Handler for the down move event + /// + public event EventHandler DownHandler = (sender, args) => { }; + + /// + /// Event handler loop + /// + /// Delegate loop exit condition + public void Run(Func EndOfСycleСondition) + { + while (!EndOfСycleСondition()) + { + var key = Console.ReadKey(true); + switch (key.Key) + { + case ConsoleKey.LeftArrow: + { + LeftHandler(this, EventArgs.Empty); + break; + } + case ConsoleKey.RightArrow: + { + RightHandler(this, EventArgs.Empty); + break; + } + case ConsoleKey.UpArrow: + { + UpHandler(this, EventArgs.Empty); + break; + } + case ConsoleKey.DownArrow: + { + DownHandler(this, EventArgs.Empty); + break; + } + case ConsoleKey.Escape: + { + return; + } + } + } + } +} + diff --git a/Game/Game/Game/Game.cs b/Game/Game/Game/Game.cs new file mode 100644 index 0000000..abb3109 --- /dev/null +++ b/Game/Game/Game/Game.cs @@ -0,0 +1,95 @@ +namespace Game; + +/// +/// The class responsible for the logic of the game +/// +public class Game +{ + private readonly Action controlFunction; + private int currentPositionOnX; + private int currentPositionOnY; + private readonly string[] map; + + /// + /// Сonstructor of Game class + /// + /// Initial position on x + /// Initial position on y + /// Path to file + /// Control function + public Game(int initialPositionOnX, int initialPositionOnY, string pathToFile, Action action) + { + currentPositionOnX = initialPositionOnX; + currentPositionOnY = initialPositionOnY; + map = File.ReadAllLines(pathToFile); + this.controlFunction = action; + PrintMap(this.map); + action(currentPositionOnX, currentPositionOnY); + DrawCharacter(); + } + + private static void DrawCharacter() => Console.WriteLine("@"); + + private static void PrintMap(string[] map) + { + for (int i = 0; i < map.Length; i++) + { + for (int j = 0; j < map[i].Length; j++) + { + Console.Write(map[i][j]); + } + Console.WriteLine(); + } + } + + private static bool IsWall(char x) => x == '|' || x == '+' || x == '-' || x == '_'; + + private void ChangePlayerPosition(Func func) + { + var (newPositionOnX, newPositionOnY) = func(currentPositionOnX, currentPositionOnY); + controlFunction(newPositionOnX, newPositionOnY); + if (IsWall(map[newPositionOnY][newPositionOnX])) + { + controlFunction(currentPositionOnX, currentPositionOnY); + return; + } + + Console.Write("@"); + controlFunction(currentPositionOnX, currentPositionOnY); + Console.WriteLine(" "); + controlFunction(newPositionOnX, newPositionOnY); + (currentPositionOnX, currentPositionOnY) = (newPositionOnX, newPositionOnY); + } + + /// + /// Function for changing the player's position one step to the left + /// + public void OnLeft(object? sender, EventArgs args) => ChangePlayerPosition((x, y) => (x - 1, y)); + + /// + /// Function for changing the player's position one step to the right + /// + public void OnRight(object? sender, EventArgs args) => ChangePlayerPosition((x, y) => (x + 1, y)); + + /// + /// Function for changing the player's position one step up + /// + public void Up(object? sender, EventArgs args) => ChangePlayerPosition((x, y) => (x, y - 1)); + + /// + /// Function for changing the player's position one step down + /// + public void Down(object? sender, EventArgs args) => ChangePlayerPosition((x, y) => (x, y + 1)); + + /// + /// Function for getting the player's position + /// + /// Player position + public (int, int) PlayerPosition() => (currentPositionOnX, currentPositionOnY); + + /// + /// A function that determines whether the game is completed or not + /// + /// True if passed + public bool IsTheEndOfTheGame() => (currentPositionOnX, currentPositionOnY) == (68, 4); // Magic 68 and 4 are the coordinates of the point you need to reach in order to win +} \ No newline at end of file diff --git a/Game/Game/Game/Game.csproj b/Game/Game/Game/Game.csproj new file mode 100644 index 0000000..74abf5c --- /dev/null +++ b/Game/Game/Game/Game.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/Game/Game/Game/Game.txt b/Game/Game/Game/Game.txt new file mode 100644 index 0000000..4dc5e68 --- /dev/null +++ b/Game/Game/Game/Game.txt @@ -0,0 +1,11 @@ ++------------------------------------------------------------------+ +| | | +| +-------------------+ +--------------------+ +---+ +----+ +| | | | | | | |____| ++---+ +--+ | | +| | | | | | +--------+ +| +-------------------+----+-----------+ +---------+ | +| | | | ++----------- +-------------------------------------+ +--------| +| | ++------------------------------------------------------------------+ diff --git a/Game/Game/Game/Program.cs b/Game/Game/Game/Program.cs new file mode 100644 index 0000000..6cabf8c --- /dev/null +++ b/Game/Game/Game/Program.cs @@ -0,0 +1,9 @@ +using Game; + +var eventLoop = new EventLoop(); +var game = new Game.Game(2, 9, "..//..//..//Game.txt", Console.SetCursorPosition); +eventLoop.LeftHandler += game.OnLeft; +eventLoop.RightHandler += game.OnRight; +eventLoop.UpHandler += game.Up; +eventLoop.DownHandler += game.Down; +eventLoop.Run(game.IsTheEndOfTheGame); \ No newline at end of file diff --git a/Game/Game/GameTest/GameTest.cs b/Game/Game/GameTest/GameTest.cs new file mode 100644 index 0000000..a45fece --- /dev/null +++ b/Game/Game/GameTest/GameTest.cs @@ -0,0 +1,138 @@ +namespace GameTest; + +using NUnit.Framework; +using System.IO; +using System; + +public class Tests +{ + private Game.Game game = new(0 ,0, "..//..//..//..//Game//Game.txt", (x, y) => { } ); + + [SetUp] + public void Setup() + { + game = new Game.Game(2, 9, "..//..//..//..//Game//Game.txt", (x, y) => { }); + } + + /// + /// It is known in advance that there is no wall on the left in the position + /// + [Test] + public void ShouldPlayerPositionOnXEqualPreviousPositionOnXMinus1WhenOnLeft() + { + var (x, y) = game.PlayerPosition(); + game.OnLeft(this, EventArgs.Empty); + var (z, t) = game.PlayerPosition(); + Assert.AreEqual(z, x - 1); + Assert.AreEqual(t, y); + } + + /// + /// It is known in advance that there is no wall on the right in the position + /// + [Test] + public void ShouldPlayerPositionOnXEqualPreviousPositionOnXPlus1WhenOnRight() + { + var (x, y) = game.PlayerPosition(); + game.OnRight(this, EventArgs.Empty); + var (z, t) = game.PlayerPosition(); + Assert.AreEqual(z, x + 1); + Assert.AreEqual(t, y); + } + + /// + /// It is known in advance that there is no wall on the up in the position + /// + [Test] + public void ShouldPlayerPositionOnYEqualPreviousPositionOnYMinus1WhenUp() + { + for (int i = 0; i < 11; i++) + { + game.OnRight(this, EventArgs.Empty); + } + + var (x, y) = game.PlayerPosition(); + game.Up(this, EventArgs.Empty); + var (z, t) = game.PlayerPosition(); + Assert.AreEqual(z, x); + Assert.AreEqual(t, y - 1); + } + + /// + /// It is known in advance that there is no wall on the down in the position + /// + [Test] + public void ShouldPlayerPositionOnYEqualPreviousPositionOnYPlus1WhenDown() + { + for (int i = 0; i < 11; i++) + { + game.OnRight(this, EventArgs.Empty); + } + + game.Up(this, EventArgs.Empty); + var (x, y) = game.PlayerPosition(); + game.Down(this, EventArgs.Empty); + var (z, t) = game.PlayerPosition(); + Assert.AreEqual(z, x); + Assert.AreEqual(t, y + 1); + } + + + /// + /// It is known in advance that in the current position there will be a wall on the left + /// + [Test] + public void ShouldPlayerPositionOnXEqualPreviousPositionOnXWhenOnLeft() + { + game.OnLeft(this, EventArgs.Empty); + var (x, y) = game.PlayerPosition(); + game.OnLeft(this, EventArgs.Empty); + var (z, t) = game.PlayerPosition(); + Assert.AreEqual(z, x); + Assert.AreEqual(t, y); + } + + /// + /// It is known in advance that in the current position there will be a wall on the right + /// + [Test] + public void ShouldPlayerPositionOnXEqualPreviousPositionOnXWhenOnRight() + { + for (int i = 0; i < 65; i ++) + { + game.OnRight(this, EventArgs.Empty); + } + + var (x, y) = game.PlayerPosition(); + game.OnRight(this, EventArgs.Empty); + var (z, t) = game.PlayerPosition(); + Assert.AreEqual(z, x); + Assert.AreEqual(t, y); + } + + /// + /// It is known in advance that in the current position there will be a wall on the up + /// + [Test] + public void ShouldPlayerPositionOnXEqualPreviousPositionOnXWhenUp() + { + var (x, y) = game.PlayerPosition(); + game.Up(this, EventArgs.Empty); + var (z, t) = game.PlayerPosition(); + Assert.AreEqual(z, x); + Assert.AreEqual(t, y); + } + + /// + /// It is known in advance that in the current position there will be a wall on the down + /// + [Test] + public void ShouldPlayerPositionOnXEqualPreviousPositionOnXWhenDown() + { + var (x, y) = game.PlayerPosition(); + game.Down(this, EventArgs.Empty); + var (z, t) = game.PlayerPosition(); + Assert.AreEqual(z, x); + Assert.AreEqual(t, y); + } +} diff --git a/Game/Game/GameTest/GameTest.csproj b/Game/Game/GameTest/GameTest.csproj new file mode 100644 index 0000000..73d4fc3 --- /dev/null +++ b/Game/Game/GameTest/GameTest.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + +