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
+
+
+
+
+
+
+
+
+
+
+
+
+
+