├── Projects
├── LunarDoggo.Beginners.ConsoleIO
│ ├── LunarDoggo.ConsoleIO.csproj
│ ├── README.md
│ └── Program.cs
├── LunarDoggo.FileSystemTree
│ ├── LunarDoggo.FileSystemTree.csproj
│ ├── README.md
│ ├── FileSystemTreeItem.cs
│ └── Program.cs
├── LunarDoggo.TicTacToe
│ ├── Properties
│ │ ├── Settings.settings
│ │ ├── Settings.Designer.cs
│ │ ├── AssemblyInfo.cs
│ │ ├── Resources.Designer.cs
│ │ └── Resources.resx
│ ├── App.xaml.cs
│ ├── README.md
│ ├── App.config
│ ├── PlayerEventArgs.cs
│ ├── App.xaml
│ ├── Player.cs
│ ├── ButtonTileMapping.cs
│ ├── PlayerTileEventArgs.cs
│ ├── MainWindow.xaml
│ ├── LunarDoggo.TicTacToe.csproj
│ ├── GameBoard.cs
│ ├── GameState.cs
│ └── MainWindow.xaml.cs
├── LunarDoggo.Beginners.ConsoleIOValidation
│ ├── LunarDoggo.ConsoleIOValidation.csproj
│ ├── README.md
│ └── Program.cs
└── LunarDoggo.QuizGame
│ ├── LunarDoggo.QuizGame.csproj
│ ├── README.md
│ ├── IO
│ ├── IQuizQuestionSerializer.cs
│ └── FileQuizQuestionSerializer.cs
│ ├── QuizQuestion.cs
│ ├── game_questions.json
│ ├── Visuals
│ ├── ConsoleVisualizer.cs
│ └── IVisualizer.cs
│ ├── Program.cs
│ ├── GameLoop.cs
│ └── GameState.cs
├── Algorithms
├── LunarDoggo.Algorithms.csproj
├── Sorting
│ ├── ISortingAlgorithm.cs
│ └── InsertionSort.cs
├── README.md
└── Graphs
│ └── Pathfinding
│ ├── BreadthFirstSearch.cs
│ └── DepthFirstSearch.cs
├── Datastructures
├── Exceptions
│ └── UnderflowException.cs
├── LunarDoggo.Datastructures.csproj
├── README.md
├── Graphs
│ ├── README.md
│ ├── Edge.cs
│ ├── IGraph.cs
│ ├── UndirectedUnweightedGraph.cs
│ └── Vertex.cs
├── Collections
│ ├── Stack.cs
│ ├── LinkedList.cs
│ ├── Queue.cs
│ └── ArrayList.cs
└── Trees
│ └── Tree.cs
├── LICENSE
├── Concepts
└── Big O notation.md
├── README.md
├── LunarDoggo.CSharpBeginnerProjects.sln
├── .editorconfig
└── .gitignore
/Projects/LunarDoggo.Beginners.ConsoleIO/LunarDoggo.ConsoleIO.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.FileSystemTree/LunarDoggo.FileSystemTree.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.Beginners.ConsoleIOValidation/LunarDoggo.ConsoleIOValidation.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace TicTacToe
4 | {
5 | ///
6 | /// Interactionlogic for "App.xaml"
7 | ///
8 | public partial class App : Application
9 | {
10 | //In this class you could override multiple methods to customize your application
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/README.md:
--------------------------------------------------------------------------------
1 | # WPF TicTacToe
2 | A small cli quiz game which loads questions and answers from a file and afterwards allows the player to answer the questions. After all questions are answered, the user is shown how many questions they got right and can restart the game.
3 |
4 | # Topics
5 | * Windows GUI with WPF
6 | * Windows Messageboxes
7 | * Events
8 | * Multidimensional arrays
--------------------------------------------------------------------------------
/Algorithms/LunarDoggo.Algorithms.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | net7.0
9 | enable
10 | enable
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Datastructures/Exceptions/UnderflowException.cs:
--------------------------------------------------------------------------------
1 | namespace LunarDoggo.Datastructures.Exceptions
2 | {
3 | ///
4 | /// An signifying that there are no items to be removed in a collection
5 | ///
6 | public class UnderflowException : Exception
7 | {
8 | public UnderflowException(string message) : base(message)
9 | { }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.QuizGame/LunarDoggo.QuizGame.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 |
7 |
8 |
9 |
10 | PreserveNewest
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.QuizGame/README.md:
--------------------------------------------------------------------------------
1 | # ConsoleIO
2 | A small cli quiz game which loads questions and answers from a file and afterwards allows the player to answer the questions. After all questions are answered, the user is shown how many questions they got right and can restart the game.
3 |
4 | # Topics
5 | * Console input without Console.Readline()
6 | * interfaces basics
7 | * System.Linq basics
8 | * serialization basics with System.Text.Json
9 | * attributes for metadata basics
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
--------------------------------------------------------------------------------
/Datastructures/LunarDoggo.Datastructures.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/PlayerEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TicTacToe
4 | {
5 | ///
6 | /// This class is used for raising events regarding a player
7 | ///
8 | public class PlayerEventArgs : EventArgs
9 | {
10 | public PlayerEventArgs(Player player)
11 | {
12 | this.Player = player;
13 | }
14 |
15 | ///
16 | /// who caused the event to be raised
17 | ///
18 | public Player Player { get; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.FileSystemTree/README.md:
--------------------------------------------------------------------------------
1 | # FileSystemTree
2 | A console application that recursively reads the file system and prints files and directories to the console.
3 |
4 | At first the user is prompted to enter a valid directory path. The input is used as used as the base directory to recursively read the filesystem into a tree structure. Afterwards the read tree is output to the console.
5 |
6 | # Topics
7 | * sequence (commands that are executed in the same order as they are written in the source-file)
8 | * loops (for, do-while)
9 | * recursion
10 | * file system read access
--------------------------------------------------------------------------------
/Algorithms/Sorting/ISortingAlgorithm.cs:
--------------------------------------------------------------------------------
1 | namespace LunarDoggo.Algorithms.Sorting
2 | {
3 | //This interface is used to keep the public interface of all sorting algorithms implemented here clean and uniform.
4 | //The type to be sorted must implement IComparable so that there is a way to tell which value is equal to, greater
5 | //or less than another one
6 | public interface ISortingAlgorithm where T : IComparable
7 | {
8 | ///
9 | /// Sorts the provided in ascending order
10 | ///
11 | void Sort(T[] values);
12 | }
13 | }
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/App.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/Player.cs:
--------------------------------------------------------------------------------
1 | namespace TicTacToe
2 | {
3 | ///
4 | /// Representation of a player
5 | ///
6 | public class Player
7 | {
8 | public Player(byte id, string display)
9 | {
10 | this.Display = display;
11 | this.Id = id;
12 | }
13 |
14 | //For tic tac toe this is usually set to either "X" or "O"
15 | ///
16 | /// Display name of the player
17 | ///
18 | public string Display { get; }
19 | ///
20 | /// Id-number of the player
21 | ///
22 | public byte Id { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.Beginners.ConsoleIO/README.md:
--------------------------------------------------------------------------------
1 | # ConsoleIO
2 | A simple console application that writes to the console-window and reads input from the user. Depending on the users input, different execution-paths can be chosen.
3 |
4 | First the user is greeted by a simple message and is prompted to input their name. If the input is empty or just consists of whitespaces, the program tells the user, that it didn't get a valid name, otherwise, the user is called by their name and wished a great day. Lastly, the application waits for any key to be pressed and terminates afterwards
5 |
6 | # Topics
7 | * sequence (commands that are executed in the same order as they are written in the source-file)
8 | * selection (if-, else-statements)
9 | * variable-declaration
10 | * variable-assignment
11 | * console output
12 | * user-input from the console
--------------------------------------------------------------------------------
/Projects/LunarDoggo.FileSystemTree/FileSystemTreeItem.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace LunarDoggo.FileSystemTree
4 | {
5 | public class FileSystemTreeItem
6 | {
7 | private readonly IEnumerable children;
8 | private readonly FileSystemTreeItemType type;
9 | private readonly string name;
10 |
11 | public FileSystemTreeItem(string name, FileSystemTreeItemType type, IEnumerable children)
12 | {
13 | this.children = children;
14 | this.name = name;
15 | this.type = type;
16 | }
17 |
18 | public IEnumerable Children { get { return this.children; } }
19 | public FileSystemTreeItemType Type { get { return this.type; } }
20 | public string Name { get { return this.name; } }
21 | }
22 |
23 | public enum FileSystemTreeItemType
24 | {
25 | Directory,
26 | File
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/ButtonTileMapping.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 |
3 | namespace TicTacToe
4 | {
5 | ///
6 | /// Represents a mapping between a and a tile with the specified coordinates
7 | ///
8 | public class ButtonTileMapping
9 | {
10 | public ButtonTileMapping(Button button, int tileX, int tileY)
11 | {
12 | this.Button = button;
13 | this.TileX = tileX;
14 | this.TileY = tileY;
15 | }
16 |
17 | ///
18 | /// of the window
19 | ///
20 | public Button Button { get; }
21 | ///
22 | /// X-coordinate of the tile
23 | ///
24 | public int TileX { get; }
25 | ///
26 | /// Y-coordinate of the tile
27 | ///
28 | public int TileY { get; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/PlayerTileEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TicTacToe
4 | {
5 | ///
6 | /// This class is used for raising events regarding a player and a specific tile on the
7 | ///
8 | public class PlayerTileEventArgs : EventArgs
9 | {
10 | public PlayerTileEventArgs(Player player, int tileX, int tileY)
11 | {
12 | this.Player = player;
13 | this.TileX = tileX;
14 | this.TileY = tileY;
15 | }
16 |
17 | ///
18 | /// who caused the event to be raised
19 | ///
20 | public Player Player { get; }
21 | ///
22 | /// X coordinate of the events tile
23 | ///
24 | public int TileX { get; }
25 | ///
26 | /// Y coordinate of the events tile
27 | ///
28 | public int TileY { get; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.Beginners.ConsoleIOValidation/README.md:
--------------------------------------------------------------------------------
1 | # ConsoleIO
2 | A simple console application that writes to the console-window and reads input from the user. Depending on the users input, different outputs are given.
3 |
4 | At first the user receives a list of games to the console-window. Afterwards the application prompts the user to select the index of a game they want to play. If the user inserts a valid index, the program prints the name of the game to the console, if the user inserts an invalid index or any non-numeric character, the application prompts the user again for an index. After the game-choice is printed, the program waits for any key to be pressed and finally terminates.
5 |
6 | # Topics
7 | * sequence (commands that are executed in the same order as they are written in the source-file)
8 | * loops (for, do-while)
9 | * static-class-variable-declaration
10 | * static-class-variable-assignment
11 | * checking if the user-input is an integer and if it is a valid index
12 | * boolean-negation (!-operator)
--------------------------------------------------------------------------------
/Datastructures/README.md:
--------------------------------------------------------------------------------
1 | # Datastructures
2 | Datastructures define a specific format to organize data with that makes storing, using and altering the data easier. Most of the time you can choose between multiple different data structures to solve a problem, but there are different data structures that excel at different jobs. Binary trees, for example, are great for searching for items, graphs are great for modelling networks and flows, and Sets are well suited for storing items that may only occur once in the collection.
3 |
4 | Many data structures are fundamental for certain algorithms, so after looking at these data structures you may want to have a look at the corresponding [algorithms](https://github.com/lunardoggo/CSharpBeginnerProjects/tree/main/Algorithms).
5 |
6 | This project showcases some basic data structures that one should know before studying advanced computer science topics. You could theoretically use these implementations in your projcest, but the data structures contained in the standard library are usually more efficient, as this project aims at explaining the basic principles and not how to optimally implement every single operation.
--------------------------------------------------------------------------------
/Algorithms/README.md:
--------------------------------------------------------------------------------
1 | # Algorithms
2 | Algorithms are an ordered collection of predefined steps for solving a specific problem. The problem scope of an algorithms usually is very narrow, but specific problems are somtimes solvable by slightly adapting a well known algorithm. Therefore it's very helpful to know the algorithms many other algorithms are based upon. For example the depth-first-search (DFS) algorithm can be adapted to check if a given graph has a valid 2-coloring or if it contains a rooted spanning tree, or even find the minimum rooted spanning tree.
3 |
4 | This project shows some basic algorithms one should know before moving on to more advanced algorithms. Some algorithms also use some basic data structures, such as graphs in case of DFS, threfor you may want to have a look at the basic data structures layed out in [data structures](https://github.com/lunardoggo/CSharpBeginnerProjects/tree/main/Datastructures) first. You could use these implementations for your own projects, but the algorithms focus on teaching basic principles and may not catch all edge cases. If the standard library provides an implementation of an algorithm you should use that instead, as the standard library usually won't cause crashes due to edge cases.
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 |
12 | namespace TicTacToe.Properties
13 | {
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
17 | {
18 |
19 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
20 |
21 | public static Settings Default
22 | {
23 | get
24 | {
25 | return defaultInstance;
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.QuizGame/IO/IQuizQuestionSerializer.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace LunarDoggo.QuizGame.IO
4 | {
5 | /*
6 | * Interfaces in C# are like blueprints for classes, every class that "implements" this interface must also define all
7 | * methods and properties of this interface as public members.
8 | * Interfaces can be used to define common methods and properties of multiple classes in order to be able to change
9 | * the instance of a concrete class in the source code without having to rewrite the application.
10 | * In the real world interfaces are for example used, when you have multiple datasources like databases, files, ...
11 | * or rendering-libraries like DirectX, OpenGL, Vulcan, ... which can be swapped in and out at runtime
12 | *
13 | * In this case, all classes that implement IQuizQuestionSerializer must declare the public method DeserializeQuestions()
14 | * that returns a IEnumerable
15 | *
16 | * Serialization is the process of converting a data structure/object into a storable format (Json, XML, BIN, ...)
17 | * Deserialization is the reverse, content of a storable format is converted into a data structure/object to be used in your application
18 | */
19 | public interface IQuizQuestionSerializer
20 | {
21 | ///
22 | /// Load all s from the datasource
23 | ///
24 | IEnumerable DeserializeQuestions();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.QuizGame/QuizQuestion.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 | using System.Linq;
3 | using System;
4 |
5 | namespace LunarDoggo.QuizGame
6 | {
7 | /*
8 | * These are simple data containers
9 | *
10 | * Note: you can define multiple classes in the same file, in theory you could define your whole application in a single file, for
11 | * small classes this is ok, for big classes with a lot of logic, it is advisable to split the classes into their own files to increase
12 | * the readability and clarity of your code
13 | */
14 | public class QuizQuestion
15 | {
16 | public QuizQuestionAnswer[] Answers { get; set; }
17 | public string Question { get; set; }
18 | /*
19 | * members of a class can have attributes that define metadata of that member. The metadata can be directed towards the compiler
20 | * (e.g. System.Runtime.CompilerServices.CallerMemberName) or can be used at runtime (e.g. JsonIgnore)
21 | *
22 | * a notable example is the attribute "System.ComponentModel.DataAnnotations.Display" which in many ui-frameworks is used to give
23 | * properties speaking names in the UI instead of the name the property has in the code
24 | *
25 | * The JsonIgnore-attribute tells the JsonSerializer if it is used by the application to ignore this attribute during serialization
26 | * and deserialization. If the application doesn't use the JsonSerializer, the attribute is still defined but never used
27 | */
28 | [JsonIgnore]
29 | public Guid Id { get; set; }
30 |
31 | public QuizQuestionAnswer CorrectAnswer { get { return this.Answers.Single(_answer => _answer.IsCorrect); } }
32 | }
33 |
34 | public class QuizQuestionAnswer
35 | {
36 | public bool IsCorrect { get; set; }
37 | public string Answer { get; set; }
38 | [JsonIgnore]
39 | public Guid Id { get; set; }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Algorithms/Sorting/InsertionSort.cs:
--------------------------------------------------------------------------------
1 | namespace LunarDoggo.Algorithms.Sorting
2 | {
3 | public class InsertionSort : ISortingAlgorithm where T : IComparable
4 | {
5 | //Insertion sort is one of the most basic sorting algorithms out there. The basic principle is
6 | //to iterate over the provided array from left to right and in each iteration step move all
7 | //items to the left that are greater than the current item one cell to the right, while
8 | //inserting the current item back into the array at the correct index.
9 | //InsertionSort has a worst-case and average-case time complexity of O(n^2), whereas, in the
10 | //best-case, the algorithm terminates after O(n) operations (for example, if the array is already
11 | //sorted in ascending order)
12 | //Additionally InsertionSort is an in situ algorithm that's stable, meaning that the sorting needs
13 | //only O(1) additional storage for the algorithm (in situ) and all groups of items that are equal
14 | //are kept in the same order
15 | public void Sort(T[] values)
16 | {
17 | //i starts at 1, as sorting only makes sense if there at least two values to be sorted
18 | for (int i = 1; i < values.Length; i++)
19 | {
20 | T key = values[i];
21 | int index = i - 1;
22 |
23 | //Move values[index] to the right if it's greater than key. This results in moving all
24 | //items that are to the left of key and are greater than key one cell to the right.
25 | while(index >= 0 && values[index].CompareTo(key) > 0)
26 | {
27 | values[index + 1] = values[index];
28 | index--;
29 | }
30 | //Lastly the value contained in "key" has to be inserted back into the array, so that
31 | //the array contains the same items after every step, but now the first i cells are sorted
32 | values[index + 1] = key;
33 | }
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/Projects/LunarDoggo.QuizGame/game_questions.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "question": "1 + 1 is:",
4 | "answers": [
5 | {
6 | "answer": "4",
7 | "isCorrect": false
8 | },
9 | {
10 | "answer": "2",
11 | "isCorrect": true
12 | },
13 | {
14 | "answer": "-7",
15 | "isCorrect": false
16 | }
17 | ]
18 | },
19 | {
20 | "question": "What is the smalles country in the world?",
21 | "answers": [
22 | {
23 | "answer": "Vatican City State",
24 | "isCorrect": true
25 | },
26 | {
27 | "answer": "Andorra",
28 | "isCorrect": false
29 | },
30 | {
31 | "answer": "Lichtenstein",
32 | "isCorrect": false
33 | }
34 | ]
35 | },
36 | {
37 | "question": "Which is the hottest planet of the solar system?",
38 | "answers": [
39 | {
40 | "answer": "Jupiter",
41 | "isCorrect": false
42 | },
43 | {
44 | "answer": "Venus",
45 | "isCorrect": true
46 | },
47 | {
48 | "answer": "Mercury",
49 | "isCorrect": false
50 | }
51 | ]
52 | },
53 | {
54 | "question": "Who discovered the radioactive elements \"polonium\" and \"radium\"?",
55 | "answers": [
56 | {
57 | "answer": "Antoine Henri Becquerel",
58 | "isCorrect": false
59 | },
60 | {
61 | "answer": "Niels Bohr",
62 | "isCorrect": false
63 | },
64 | {
65 | "answer": "Marie Curie",
66 | "isCorrect": true
67 | }
68 | ]
69 | },
70 | {
71 | "question": "Which solar system planet has the lowest overall density?",
72 | "answers": [
73 | {
74 | "answer": "Saturn",
75 | "isCorrect": true
76 | },
77 | {
78 | "answer": "Jupiter",
79 | "isCorrect": false
80 | },
81 | {
82 | "answer": "Neptune",
83 | "isCorrect": false
84 | }
85 | ]
86 | },
87 | {
88 | "question": "The solar system object \"Pluto\" is classified as a:",
89 | "answers": [
90 | {
91 | "answer": "Planet",
92 | "isCorrect": false
93 | },
94 | {
95 | "answer": "Dwarf planet",
96 | "isCorrect": true
97 | },
98 | {
99 | "answer": "Comet",
100 | "isCorrect": false
101 | }
102 | ]
103 | }
104 | ]
--------------------------------------------------------------------------------
/Datastructures/Graphs/README.md:
--------------------------------------------------------------------------------
1 | # Graphs
2 | In the field of graph theory, a _**graph**_ is an abstract structure that represents connections between objects. A graph at least consists of a Tuple _G=(V,E)_ of Vertices (V) and Edges (E).
3 |
4 | Graphs have different properties you should know before trying to understand the algorithms that use this datastructure, as every algorithm only works with special kinds of graphs. This project will mainly focus on simple graphs:
5 | * A graph is either a **simple graph** or a **multi graph**. In simple graphs there may only be one edge between a given pair of vertices and three cannot be an edge from a vertex to itself (loop), whereas multi graphs allow for both an arbitrary number of edges between a given pair of vertices and loops.
6 | * A graph is either **directed** or **undirected**. Whereas edges in undirected graphs can be traversed in any direction, in directed graphs there may be edges that only allow for one-way traversal
7 | * A graph is either **weighted** or **unweighted**. A weighted graph uses a function _c(e)_ to assign a number/weight to every edge of the graph, the notation usually changes to _G=(V,E,c(e))_.
8 | * A graph is either **cyclic** or **acyclic**. Depending on if the graph is _directed_ or _undirected_, this property has different meanings:
9 | * An _undirected_ graph is acyclic if there is no pair of vertices _u,v_ in _V_, so that there is more than one distinct path from _u_ to _v_ or vice versa
10 | * A _directed_ graph is acyclic if there is vertex _v_ in _V_ for which there is a directed path of length greater than one that starts in _v_ and ends in _v_
11 | * An _undirected_ graph is called **connected** if there is a path from every Vertex _u_ in _V_ to every other Vertex _v_ in _V_. If a graph isn't connected, it's called **disconnected**
12 | * An _undirected_ graph is called **complete** if there is an edge _{u,v}_ between every pair of Vertices _u,v_ in _V_
13 | * A **bipartite** graph is a graph where the set of vertices _V_ can be divided into two non-empty, disjunct subsets _S_ and _T_ of _V_ where there is no edge connecting any two vertices in _S_ and there is no edge connecting any two vertices in _T_.
14 | * A **planar** graph is a graph that can be drawn _in a plane_ without crossing any edges except at their start/end vertex
15 |
16 | For this project specifically you should also keep in mind that, as the focus lies on simple graphs, most graphs will at most have O(|V|^2) edges which facilitates determine the time complexity of graph algorithms.
--------------------------------------------------------------------------------
/Datastructures/Graphs/Edge.cs:
--------------------------------------------------------------------------------
1 | namespace LunarDoggo.Datastructures.Graphs
2 | {
3 | //This class also has to be generic, as Vertex is already generic and this class depends on Vertex
4 | //Implementing IEquatable> helps to compare different instances of Edge for equal values
5 | public class Edge
6 | {
7 | ///
8 | public Edge(Vertex from, Vertex to, bool bidirectional)
9 | {
10 | //this check helps to avoid NullReferenceExceptions down the line
11 | if (from is null || to is null)
12 | {
13 | throw new ArgumentNullException("None of the vertices of an edge can be null!");
14 | }
15 |
16 | this.IsBidirectional = bidirectional;
17 | this.From = from;
18 | this.To = to;
19 | }
20 |
21 | ///
22 | /// Returns whether this edge is bidirecional (i.e. if it's undirected)
23 | ///
24 | public bool IsBidirectional { get; }
25 | ///
26 | /// Returns the the edge starts at
27 | ///
28 | public Vertex From { get; }
29 | ///
30 | /// Returns the the edge ends at
31 | ///
32 | public Vertex To { get; }
33 |
34 | //Equals() is overriden because HashSets are used to store edges and therefore it's easier to avoid
35 | //duplicate entries if edges containing the same values are considered equal instead of relying on
36 | //reference equality
37 | public override bool Equals(object? obj)
38 | {
39 | //If the provided object is an edge all edge properties are compared. As there can be multiple vertices containing the same
40 | //value, two vertices are considered distinct, even if they contain the same value. As a result the Equals() method of
41 | //Vertex isn't overriden with a custom implementation
42 | return obj is Edge edge && edge.From == this.From && edge.To == this.To && edge.IsBidirectional == this.IsBidirectional;
43 | }
44 |
45 | //As the Equals method is overriden, GetHashCode has to be overriden as well
46 | public override int GetHashCode()
47 | {
48 | //Since dotnet core 2.1 there is HashCode.Combine() which facilitates creating hash codes from multiple objects
49 | return HashCode.Combine(this.From, this.To, this.IsBidirectional);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // Allgemeine Informationen über eine Assembly werden über die folgenden
8 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
9 | // die einer Assembly zugeordnet sind.
10 | [assembly: AssemblyTitle("TicTacToe")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("TicTacToe")]
15 | [assembly: AssemblyCopyright("Copyright © 2021")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
20 | // für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
21 | // COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
22 | [assembly: ComVisible(false)]
23 |
24 | //Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie
25 | //ImCodeVerwendeteKultur in der .csproj-Datei
26 | //in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch
27 | //(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung
28 | //des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile,
29 | //sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher
36 | //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
37 | // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.)
38 | ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs
39 | //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird,
40 | // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.)
41 | )]
42 |
43 |
44 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
45 | //
46 | // Hauptversion
47 | // Nebenversion
48 | // Buildnummer
49 | // Revision
50 | //
51 | // Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
52 | // indem Sie "*" wie unten gezeigt eingeben:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/Datastructures/Graphs/IGraph.cs:
--------------------------------------------------------------------------------
1 | namespace LunarDoggo.Datastructures.Graphs
2 | {
3 | public interface IGraph
4 | {
5 | ///
6 | /// Returns all vertices contained in this graph
7 | ///
8 | IEnumerable> Vertices { get; }
9 | ///
10 | /// Returns all edges contained in this graph
11 | ///
12 | IEnumerable> Edges { get; }
13 |
14 | ///
15 | /// Removes the provided from this graph
16 | ///
17 | void RemoveVertex(Vertex vertex);
18 | ///
19 | /// Adds a new vertex containing the provided to the graph and returns the
20 | /// newly created vertex
21 | ///
22 | Vertex AddVertex(T value);
23 |
24 | ///
25 | /// Removes an existing Edge from to from the graph
26 | ///
27 | void RemoveEdge(Vertex from, Vertex to);
28 | }
29 |
30 | //Unweighted graphs can also be described as graphs where every edge has the same weight of a constant c.
31 | //This project uses two different interfaces to represent graphs noneteless, as unweighted graphs would
32 | //need to implement a parameter "weight" when adding edges, that the graph doesn't need and therefore
33 | //shouldn't implement
34 |
35 | ///
36 | /// Represents an unweighted graph that contains vertices containing values of type T
37 | ///
38 | public interface IUnweightedGraph : IGraph
39 | {
40 | ///
41 | /// Adds a new Edge from to to the graph. If the edge is
42 | /// directed or undirected depends on the implementation used
43 | ///
44 | ///
45 | Edge AddEdge(Vertex from, Vertex to);
46 | }
47 |
48 | ///
49 | /// Represents a weighted graph that contains vertices containing values of type T
50 | ///
51 | public interface IWeightedGraph : IGraph
52 | {
53 | ///
54 | /// Adds a new Edge from to with a weight of
55 | /// to the graph. If the edge is directed or undirected depends on the implementation used
56 | ///
57 | ///
58 | Edge AddEdge(Vertex from, Vertex to, float weight);
59 | ///
60 | /// Returns the weight of the provided
61 | ///
62 | ///
63 | float GetWeight(Edge edge);
64 | }
65 | }
--------------------------------------------------------------------------------
/Projects/LunarDoggo.QuizGame/Visuals/ConsoleVisualizer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LunarDoggo.QuizGame.Visuals
4 | {
5 | public class ConsoleVisualizer : IVisualizer
6 | {
7 | public ConsoleVisualizer()
8 | {
9 | //this call hides the white underscore indicator of the cursor
10 | Console.CursorVisible = false;
11 | }
12 |
13 | public void DrawNoQuestions()
14 | {
15 | Console.WriteLine("No questions were loaded, please enter some questions into the json file in the applications folder.\n\nReload game? (Y/N)");
16 | }
17 |
18 | public void DrawAnswerStatus(bool correct, QuizQuestionAnswer correctAnswer)
19 | {
20 | if (correct)
21 | {
22 | Console.WriteLine("Your answer is correct. Continue with \"enter\".");
23 | }
24 | else
25 | {
26 | Console.WriteLine("Your answer isn't correct. The correct answer is: \"{0}\". Continue with \"enter\".", correctAnswer.Answer);
27 | }
28 | }
29 |
30 | public void DrawQuizQuestion(QuizQuestion question, Guid highlitedAnswerId)
31 | {
32 | Console.Clear();
33 | Console.WriteLine(question.Question);
34 | Console.WriteLine();
35 | foreach (QuizQuestionAnswer answer in question.Answers)
36 | {
37 | this.DrawQuizQuestionAnswer(answer, answer.Id == highlitedAnswerId);
38 | }
39 | Console.WriteLine();
40 | Console.WriteLine();
41 | }
42 |
43 | private void DrawQuizQuestionAnswer(QuizQuestionAnswer answer, bool highlited)
44 | {
45 | Console.SetCursorPosition(1, Console.CursorTop);
46 | //you can write on line conditional statements in the format: {boolean expression} ? {action when the condition is met} : {action when the condition isn't met}
47 | Console.WriteLine("({0}) {1}", highlited ? "*" : " ", answer.Answer);
48 | //in this case, the previous line could also be written as:
49 | //if(highlighted) { Console.WriteLine("({0}) {1}", "*", answer.Answer); } else { Console.WriteLine("({0}) {1}", " ", answer.Answer); }
50 | }
51 |
52 | public void DrawGameStart(int totalQuestionCount)
53 | {
54 | Console.Clear();
55 | Console.WriteLine("{0} question{1} {2} loaded, press \"enter\" to start the game.", totalQuestionCount, totalQuestionCount > 1 ? "s" : "", totalQuestionCount > 1 ? "were" : "was");
56 | }
57 |
58 | public void DrawGameResult(int totalQuestionCount, int correctAnswersCount)
59 | {
60 | Console.WriteLine("You got {0} out of {1} question right. Continue with \"enter\".", correctAnswersCount, totalQuestionCount);
61 | Console.WriteLine();
62 | }
63 |
64 | public void DrawPlayAgain()
65 | {
66 | Console.WriteLine("Do you like to play again? (Y/N)");
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.QuizGame/Program.cs:
--------------------------------------------------------------------------------
1 | using LunarDoggo.QuizGame.Visuals;
2 | using System.Collections.Generic;
3 | using LunarDoggo.QuizGame.IO;
4 | using System.Linq;
5 | using System;
6 |
7 | namespace LunarDoggo.QuizGame
8 | {
9 | public class Program
10 | {
11 | //The declaration with the interface IVisualizer instead of declaring it as a ConsoleVisualizer has the advantage that we,
12 | //if we were to implement another visualizer for, example with OpenGL, we could swap it without rewriting the application
13 | public static IVisualizer Visualizer { get; } = new ConsoleVisualizer();
14 |
15 | private static void Main()
16 | {
17 | bool run;
18 |
19 | do
20 | {
21 | //First load the questions from the datasource, if the questions change between the do-while-loop-iteration,
22 | //the changes are reflected in the next run of the loop
23 | IEnumerable questions = Program.GetQuestions();
24 | //if questions were loaded, start the game, otherwise prompt the user to update the question-datasource
25 | if (questions.Any()) //Note: .Any() is part of the extension methods from System.Linq and returns whether there are any entries in the questions collection
26 | {
27 | Program.RunGameLoop(questions);
28 | }
29 | else
30 | {
31 | Program.Visualizer.DrawNoQuestions();
32 | }
33 |
34 | //If the user presses the "Y"-Key, we want to run the loop again, otherwise, the application terminates
35 | run = Console.ReadKey().Key == ConsoleKey.Y;
36 | } while (run);
37 | }
38 |
39 | ///
40 | /// Instantiate and run the with the provided s
41 | ///
42 | private static void RunGameLoop(IEnumerable questions)
43 | {
44 | GameLoop gameLoop = new GameLoop(Program.Visualizer, questions);
45 | //while the game isn't finished, we want to draw to the screen run updates to the gamestate and process user input
46 | while (!gameLoop.IsFinished)
47 | {
48 | gameLoop.DoTick();
49 | }
50 | //when the game is finished, we want to ask the user if they want to play again
51 | Program.Visualizer.DrawPlayAgain();
52 | }
53 |
54 | ///
55 | /// Load all questions from the datasource
56 | ///
57 | private static IEnumerable GetQuestions()
58 | {
59 | string filePath = ".\\game_questions.json";
60 | //if we were to implement another datasource (e.g. MySQLQuizQuestionSerializer) we could swap it here
61 | IQuizQuestionSerializer serializer = new FileQuizQuestionSerializer(filePath);
62 | return serializer.DeserializeQuestions();
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Concepts/Big O notation.md:
--------------------------------------------------------------------------------
1 | # Landau Notation
2 | Landau notation (or big O notation) describes describes the growth of a particular function. In computer science it's often used to analyze and classify algorithms according to their so called "time complexity" and "space complexity".
3 |
4 | # Symbols
5 | The following symbols are used to denote the growth of a function **f**:
6 | - **O(g(n))** (big O): means that the function **f** grows as fast or slower than **g** for big **n**
7 | - **Θ(g(n))** (big Theta): means that the function **f** grows exactly as fast as **g** for big **n**
8 | - **Ω(g(n))** (big Omega): means that the function **f** grows at least as fast as **g** for big **n**
9 | - **o(g(n))** (small O): means that the function **f** grows slower than **g** for big **n**
10 | - **ω(g(n))** (small omega): means that the function **f** grows faster than **g** for big **n**
11 |
12 | Big O is most commonly used in practice, but Θ and Ω also occur sometimes. The other two (o, ω) are used quite rarely in algorithm analysis. Some important things to note are that:
13 | 1. all of these notations only make sense if **n** approaches infinity, as, for example, the time difference between the execution of two algorithms is almost zero for a small dataset in practice and there may be cases where **O(f(n)) > O(g(n))**, but **f(n) < g(n)** for small **n** values
14 | 2. there is a certain order of which function grows faster as another one, such as **O(1) < O(n) < O(n * log(n)) < O(n^2) < O(2^n)**
15 | 3. an algorithm is most commonly considered efficient if it has a polynomial worst case time complexity. The biggest exponent of the function is irrelevant for that definition, **O(n)** is efficient, as well as **O(n^10000000)**
16 | 4. there are rules for combinding multiple big **O** (or one of the other notations, as long as all symbols match) notations into one:
17 | - multiplicative constants can be ignored: **O(n) = O(2n)**
18 | - **O(a) + O(b) = O(a + b)**, if **O(a) > O(b)**, **O(b)** can be omitted, if **O(a) < O(b)**, **O(a)** can be omitted, if **O(a) = O(b)**, either one can be omitted. You can see this rule in action when using graphs, as a time complexity of **O(|V| + |E|)** could be reduced to **O(|V|^2)**, as **O(|E|) < O(|V|^2)** in general, but there are graphs where **O(|E|) = O(|V|)**, so it's sometimes better to keep both around to make the upper bound more strict
19 | - **O(a) * O(b) = O(a * b)**, this rule is for example used when analyzing loops. If the body of a loop has a time complexity of **O(a)** and the loop runs **O(b)** times, there are **O(b)** occurrences of **O(a)** in the analysis, so you would multiply them
20 | 5. You can analyze the best- average- and worst-case time/space complexity separately, for example **InsertionSort** has a best case time complexity of **O(n)** where **n** is the amount of items in the sorted array, but the average- and worst-case time complexity is **O(n^2)**
21 |
22 | Concrete examples and further clarification for points **1.** and **2.** can be found on [libretexts.org](https://eng.libretexts.org/Bookshelves/Computer_Science/Programming_Languages/Think_Python_-_How_to_Think_Like_a_Computer_Scientist_(Downey)/13%3A_Appendix_B-_Analysis_of_Algorithms/13.01%3A_Order_of_Growth)
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // Dieser Code wurde von einem Tool generiert.
4 | // Laufzeitversion: 4.0.30319.42000
5 | //
6 | // Änderungen an dieser Datei können fehlerhaftes Verhalten verursachen und gehen verloren, wenn
7 | // der Code erneut generiert wird.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 |
12 | namespace TicTacToe.Properties
13 | {
14 | ///
15 | /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
16 | ///
17 | // Diese Klasse wurde von der StronglyTypedResourceBuilder-Klasse
18 | // über ein Tool wie ResGen oder Visual Studio automatisch generiert.
19 | // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
20 | // mit der Option /str erneut aus, oder erstellen Sie Ihr VS-Projekt neu.
21 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
22 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
23 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
24 | internal class Resources
25 | {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources()
33 | {
34 | }
35 |
36 | ///
37 | /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
38 | ///
39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
40 | internal static global::System.Resources.ResourceManager ResourceManager
41 | {
42 | get
43 | {
44 | if ((resourceMan == null))
45 | {
46 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TicTacToe.Properties.Resources", typeof(Resources).Assembly);
47 | resourceMan = temp;
48 | }
49 | return resourceMan;
50 | }
51 | }
52 |
53 | ///
54 | /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
55 | /// Ressourcenlookups, die diese stark typisierte Ressourcenklasse verwenden.
56 | ///
57 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
58 | internal static global::System.Globalization.CultureInfo Culture
59 | {
60 | get
61 | {
62 | return resourceCulture;
63 | }
64 | set
65 | {
66 | resourceCulture = value;
67 | }
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.QuizGame/Visuals/IVisualizer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LunarDoggo.QuizGame.Visuals
4 | {
5 | /*
6 | * Interfaces in C# are like blueprints for classes, every class that "implements" this interface must also define all
7 | * methods and properties of this interface as public members.
8 | * Interfaces can be used to define common methods and properties of multiple classes in order to be able to change
9 | * the instance of a concrete class in the source code without having to rewrite the application.
10 | * In the real world interfaces are for example used, when you have multiple datasources like databases, files, ...
11 | * or rendering-libraries like DirectX, OpenGL, Vulcan, ... which can be swapped in and out at runtime
12 | *
13 | * In this case, all classes that implement IQuizQuestionSerializer must declare the following public methods as voids:
14 | * DrawAnswerStatus(bool correct, QuizQuestionAnswer correctAnswer)
15 | * DrawQuizQuestion(QuizQuestion question, Guid highlitedAnswerId)
16 | * DrawGameResult(int totalQuestionCount, int correctAnswersCount)
17 | * DrawGameStart(int totalQuestionCount)
18 | * DrawNoQuestions()
19 | * DrawPlayAgain()
20 | */
21 | public interface IVisualizer
22 | {
23 | ///
24 | /// Draws if the answer the user gave was correct or wrong
25 | ///
26 | /// bool-indicator if the last given answer was correct
27 | /// answer to be displayed if correct is set to false
28 | void DrawAnswerStatus(bool correct, QuizQuestionAnswer correctAnswer);
29 |
30 | ///
31 | /// Draws a to the display and highlights the currenctly selected
32 | /// where equals
33 | ///
34 | /// question to be shown
35 | /// to be highlighted
36 | void DrawQuizQuestion(QuizQuestion question, Guid highlitedAnswerId);
37 |
38 | ///
39 | /// After the game is finished, this method draws the overall result
40 | ///
41 | /// Count of all asked questions
42 | /// Cound of questions the user got correct
43 | void DrawGameResult(int totalQuestionCount, int correctAnswersCount);
44 |
45 | ///
46 | /// Before the game starts, this method draws the count of answers loaded from the file
47 | ///
48 | void DrawGameStart(int totalQuestionCount);
49 |
50 | ///
51 | /// When no questions could be loaded from the file, this method tells the user to update the json question file
52 | ///
53 | void DrawNoQuestions();
54 |
55 | ///
56 | /// When the game is finished, this method asks the user if they want to play another round of the game
57 | ///
58 | void DrawPlayAgain();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.QuizGame/IO/FileQuizQuestionSerializer.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json;
3 | using System.IO;
4 | using System;
5 | using System.Text.Json.Serialization;
6 |
7 | namespace LunarDoggo.QuizGame.IO
8 | {
9 | /*
10 | * In this case, FileQuizQuestionSerializer implements the interface IQuizQuestionSerializer so that other
11 | * serializers (for example database) can be implemented to swap between the implementations
12 | */
13 | public class FileQuizQuestionSerializer : IQuizQuestionSerializer
14 | {
15 | private readonly string filePath;
16 |
17 | /// Absolute or relative file path to the quiz question json file
18 | public FileQuizQuestionSerializer(string filePath)
19 | {
20 | this.filePath = filePath;
21 | }
22 |
23 | public IEnumerable DeserializeQuestions()
24 | {
25 | string content = this.GetFileContent(this.filePath);
26 | IEnumerable questions = this.DeserializeJson(content);
27 | this.SetGuids(questions);
28 | return questions;
29 | }
30 |
31 | ///
32 | ///We don't trust the user to correctly set the Ids of the and . In order to prevent duplicate Ids
33 | ///this method assigns a unique to every and its s
34 | ///
35 | private void SetGuids(IEnumerable questions)
36 | {
37 | foreach (QuizQuestion question in questions)
38 | {
39 | //Guid.NewGuid() generates a new random Guid
40 | question.Id = Guid.NewGuid();
41 | foreach (QuizQuestionAnswer answer in question.Answers)
42 | {
43 | answer.Id = Guid.NewGuid();
44 | }
45 | }
46 | }
47 |
48 | private IEnumerable DeserializeJson(string content)
49 | {
50 | JsonSerializerOptions options = new JsonSerializerOptions()
51 | {
52 | DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, //if a property has the value "null", it will be ignored
53 | ReadCommentHandling = JsonCommentHandling.Skip, //when the json content contains comments, they will be ignored
54 | PropertyNameCaseInsensitive = true, //the casing of propertynames will be ignored for the deserialization
55 | AllowTrailingCommas = true //if the json content contains a lonely ",", it will be ignored
56 | };
57 |
58 | //System.Text.Json.JsonSerializer deserializes the content string into a QuizQuestion array
59 | return JsonSerializer.Deserialize(content, options);
60 | }
61 |
62 | private string GetFileContent(string filePath)
63 | {
64 | //Check if the file exists, if not, return an empty string to prevent a FileNotFoundException, otherwise return the files content
65 | //Note that file access violations (e. g. another application has the file locked) are not handled and will lead to an exception
66 | if (File.Exists(filePath))
67 | {
68 | return File.ReadAllText(filePath);
69 | }
70 | return String.Empty;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.FileSystemTree/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.IO;
5 | using System;
6 |
7 | namespace LunarDoggo.FileSystemTree
8 | {
9 | class Program
10 | {
11 | static void Main(string[] args)
12 | {
13 | string baseDirectoryPath = Program.GetBaseDirectoryPath();
14 | DirectoryInfo baseDirectory = new DirectoryInfo(baseDirectoryPath);
15 |
16 | FileSystemTreeItem fileSystemTree = Program.GetFileSystemTree(baseDirectory);
17 |
18 | Program.OutputFileSystemTreeLevel(0, fileSystemTree);
19 | }
20 |
21 | private static void OutputFileSystemTreeLevel(int indentationLevel, FileSystemTreeItem item)
22 | {
23 | //for each indentationlevel we add two spaces
24 | string indentation = new string(Enumerable.Repeat(' ', indentationLevel * 2).ToArray());
25 |
26 | //combine the indentation with the current tree items name and type
27 | Console.WriteLine(indentation + item.Name + " (" + item.Type + ")");
28 |
29 | //if the current tree item has any children, recursively print them and
30 | //their children to the console with the corresponding indentatino level
31 | if (item.Children != null && item.Children.Count() > 0)
32 | {
33 | foreach (FileSystemTreeItem child in item.Children)
34 | {
35 | Program.OutputFileSystemTreeLevel(indentationLevel + 1, child);
36 | }
37 | }
38 | }
39 |
40 | ///
41 | /// Recursive Method to get the file system structure as a tree
42 | ///
43 | private static FileSystemTreeItem GetFileSystemTree(DirectoryInfo baseDirectory)
44 | {
45 | //Read all subdirectories and files from the current baseDirectory
46 | DirectoryInfo[] subdirectories = baseDirectory.GetDirectories();
47 | FileInfo[] files = baseDirectory.GetFiles();
48 |
49 | List children = new List();
50 |
51 | //First recursively add all subdirectories with its children to the current tree item
52 | foreach (DirectoryInfo subdirectory in subdirectories)
53 | {
54 | //add all tree items from
55 | children.Add(Program.GetFileSystemTree(subdirectory));
56 | }
57 |
58 | //Lastly add all files of the current tree item
59 | foreach (FileInfo file in files)
60 | {
61 | children.Add(new FileSystemTreeItem(file.Name, FileSystemTreeItemType.File, null));
62 | }
63 |
64 | return new FileSystemTreeItem(baseDirectory.Name, FileSystemTreeItemType.Directory, children.ToImmutableArray());
65 | }
66 |
67 | ///
68 | /// Get the base directory path from user input
69 | ///
70 | private static string GetBaseDirectoryPath()
71 | {
72 | string path;
73 | do
74 | {
75 | Console.Clear(); //Clear the console window
76 | Console.Write("Please enter a valid directory path: ");
77 | path = Console.ReadLine();
78 |
79 | //While the user input is not a valid path and therefore doesn't exist, continue to prompt for a valid directory path
80 | } while (!Directory.Exists(path));
81 | return path;
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Datastructures/Collections/Stack.cs:
--------------------------------------------------------------------------------
1 | using LunarDoggo.Datastructures.Exceptions;
2 |
3 | namespace LunarDoggo.Datastructures.Collections
4 | {
5 | //A stack is a so called LIFO (last in, first out) data structure that can be imagined as a pile of objects
6 | //stacked on one and another. If you want to put something on the pile you put it on the top, and if you
7 | //want to retrieve an object that's in the middle of the stack you have to remove all the objects that are
8 | //on top of the object to be retrieved.
9 | //The most primitive implementation uses an array that's not resized over the stack's lifetime. If the stack
10 | //runs full and another object is added, an exception is thrown signifying that no more items can be pushed
11 | //onto the stack
12 | public class Stack
13 | {
14 | //-1 means that there are no items in the cache yet
15 | private int currentIndex = -1;
16 | private readonly T[] cache;
17 |
18 | public Stack(int capacity)
19 | {
20 | //The initialization of the stack has a time complexity of O(capacity), as C# not only allocates a certain memory
21 | //range, but also sets all cells of the array to the default value of type T
22 |
23 | //As the array will never be resized or reallocated, the capacity of the stack has to be provided on initialization
24 | this.cache = new T[capacity];
25 | }
26 |
27 | ///
28 | /// Pushes another item of type onto the stack
29 | ///
30 | ///
31 | public void Push(T item)
32 | {
33 | //Push has a time complexity of O(1), as only constant time operations are used sequentially
34 | if (currentIndex >= this.cache.Length - 1)
35 | {
36 | throw new OverflowException("The stack is already full");
37 | }
38 |
39 | this.currentIndex++;
40 | this.cache[this.currentIndex] = item;
41 | }
42 |
43 | ///
44 | /// Removes the item on top of the stack from the stack and returns the previously contained value
45 | ///
46 | ///
47 | public T Pop()
48 | {
49 | //Pop has a time complexity of O(1), as only constant time operations are used sequentially
50 | T output = this.Peek();
51 | //usually the cells of removed values aren't reset to the default value of T, but this could
52 | //result in security issues, as sensitive deleted data isn't garbage collected as soon as possible
53 | this.cache[this.currentIndex] = default;
54 | return output;
55 | }
56 |
57 | ///
58 | /// Returns the item that's on top of the stack
59 | ///
60 | ///
61 | public T Peek()
62 | {
63 | //Peek has a time complexity of O(1), as only constant time operations are used sequentially
64 | if (this.IsEmpty)
65 | {
66 | throw new UnderflowException("The stack is empty");
67 | }
68 |
69 | return this.cache[this.currentIndex];
70 | }
71 |
72 | ///
73 | /// Returns whether the stack is empty
74 | ///
75 | public bool IsEmpty
76 | {
77 | //IsEmpty has a time complexity of O(1), as only constant time operations are used sequentially
78 | get => this.currentIndex < 0;
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs:
--------------------------------------------------------------------------------
1 | using LunarDoggo.Datastructures.Graphs;
2 |
3 | namespace LunarDoggo.Algorithms.Graphs.Pathfinding
4 | {
5 | //BFS is a basic pathfinding algorithm that works on unweighted graphs. It doesn't matter if the graph is directed
6 | //or undirected. The time complexity of BFS is O(|V| + |E|), as the algorithm processes every vertex and iterates over every edge in the graph exactly once
7 | //In simple graphs |E| is bounded by O(|V|^2), so in such cases, one could use O(|V|^2) as the time complexity
8 | public class BreadthFirstSearch
9 | {
10 | public void Run(IGraph graph, Vertex start)
11 | {
12 | if (graph is null)
13 | {
14 | throw new ArgumentNullException("The graph the DFS should be run on cannot be null");
15 | }
16 |
17 | this.Initialize(graph, start);
18 |
19 | //BFS is usually implemented in a way that doesn't work on disconnected graphs. BFS can therefore be
20 | //used to find all vertices contained in a connected component of the graph.
21 | //BFS additionally is implemented iteratively using a queue. One could imagine the execution as a wave that propagates from the starting vertex to all reachable vertices of the graph
22 | Queue> queue = new Queue>();
23 | queue.Enqueue(start);
24 | while (queue.Count > 0)
25 | {
26 | Vertex current = queue.Dequeue();
27 | foreach (Vertex vertex in current.Adjacent)
28 | {
29 | //If a processed adjacent vertex is found, it doesn't have to be processed again, as the distance from the start cannot be lower
30 | //than the currently set distance
31 | if (!vertex.Value.Processed)
32 | {
33 | vertex.Value.Distance = current.Value.Distance + 1;
34 | vertex.Value.Predecessor = current;
35 | vertex.Value.Processed = true;
36 | }
37 | }
38 | }
39 | }
40 |
41 | private void Initialize(IGraph graph, Vertex start)
42 | {
43 | //Every vertex is initialized with the maximum possible distance to signify that the distance is invalid.
44 | foreach (Vertex vertex in graph.Vertices)
45 | {
46 | vertex.Value = new BFSVertex()
47 | {
48 | Distance = Int32.MaxValue,
49 | Predecessor = null,
50 | Processed = false
51 | };
52 | }
53 |
54 | //The starting vertex, is guaranteed to be reachable from the start with a distance of 0 edges. The start also
55 | //doesn't have a predecessor, as it is an end vertex of every found start-vertex-path
56 | start.Value.Processed = true;
57 | start.Value.Distance = 0;
58 | }
59 | }
60 |
61 | public class BFSVertex
62 | {
63 | ///
64 | /// Returns whether this vertex has already been processed. The processed flag has to be set by the algorithm
65 | ///
66 | public bool Processed { get; set; }
67 | ///
68 | /// Returns the predecessor of this vertex on the path from the starting vertex
69 | ///
70 | public Vertex Predecessor { get; set; }
71 | ///
72 | /// Returns the distance in edges of this vertex to the start vertex
73 | ///
74 | public int Distance { get; set; }
75 | }
76 | }
--------------------------------------------------------------------------------
/Datastructures/Graphs/UndirectedUnweightedGraph.cs:
--------------------------------------------------------------------------------
1 | namespace LunarDoggo.Datastructures.Graphs
2 | {
3 | public class UndirectedUnweightedGraph : IUnweightedGraph
4 | {
5 | //Using HashSets for storing the vertices and edges, again, facilitates managing the collections more easily,
6 | //as it makes removing and checking for entries easier while avoiding duplicate entries
7 | private HashSet> vertices = new HashSet>();
8 | private HashSet> edges = new HashSet>();
9 |
10 | //If the original HashSet was returned, it would be possible to modify the original collection of vertices/edges
11 | //therefore the LINQ method ToArray() is called to return a copy of the original sets
12 | public IEnumerable> Vertices { get => this.vertices.ToArray(); }
13 | public IEnumerable> Edges { get => this.edges.ToArray(); }
14 |
15 | private int lastId = 0;
16 |
17 | public Edge AddEdge(Vertex from, Vertex to)
18 | {
19 | //this lock ensures thread safety when adding edges, as multithreaded Add operations without locks can lead to null
20 | //entries in the collection
21 | lock (this.edges)
22 | {
23 | //The ArgumentNullException advertised in the definition of AddEdge in IUnweightedGraph will be thrown by the
24 | //constructor of Edge, therefore it isn't necessary to check from and to for null values here
25 | Edge edge = new Edge(from, to, true);
26 |
27 | //As the edge and adjacencies are stored in HashSets, it isn't necessary to check if it already exists
28 | this.edges.Add(edge);
29 | from.AddAdjacency(to);
30 | to.AddAdjacency(from);
31 |
32 | return edge;
33 | }
34 | }
35 |
36 | public Vertex AddVertex(T value)
37 | {
38 | //lock is used to ensure thread safety when incrementing lastId. The lock is set on vertices because
39 | //locks can only be set on reference types and lastId, as an Int32, is a ValueType
40 | lock (this.vertices)
41 | {
42 | Vertex vertex = new Vertex(this.lastId, value);
43 | //If an exception ocurrs when adding a vertex, the increment to lastId will not happen, which is intended behavior
44 | this.vertices.Add(vertex);
45 | this.lastId++;
46 | return vertex;
47 | }
48 | }
49 |
50 | public void RemoveEdge(Vertex from, Vertex to)
51 | {
52 | lock (this.edges)
53 | {
54 | //As edges that contain the same values are considered equal and all edges are stored in a HashSet,
55 | //edges can be removed by removing a new and equal edge from edges
56 | Edge edge = new Edge(from, to, true);
57 | this.edges.Remove(edge);
58 | //The adjacency lists of the vertices also have to be updated
59 | from.RemoveAdjacency(to);
60 | to.RemoveAdjacency(from);
61 | }
62 | }
63 |
64 | public void RemoveVertex(Vertex vertex)
65 | {
66 | lock (this.vertices)
67 | {
68 | this.vertices.Remove(vertex);
69 | //the provided vertex is only removed from the adjacency lists of the remaining vertices without clearing
70 | //its own adjacency list, as it is no longer part of the graph and will likely be garbage collected soon
71 | foreach (Vertex v in this.vertices)
72 | {
73 | v.RemoveAdjacency(vertex);
74 | }
75 | }
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/Datastructures/Trees/Tree.cs:
--------------------------------------------------------------------------------
1 | namespace LunarDoggo.Datastructures.Trees
2 | {
3 | //Trees are a special kind of graph: they're connected, undirected, acyclic and unweighted. In general trees konsist of
4 | //Individual vertices (thereafter "TreeItems") that may have an arbitrary amount of children
5 | //The traversal of trees is similar to that of doubly linked lists with the difference that each item may have an arbitrary
6 | //amount of successors instead of a maximum of one.
7 |
8 | //Note: one could also declare a class called Tree, but it would only contain a reference to the root and maybe
9 | // some operations that aren't considered standard for trees, so instead one would store a reference to the root
10 | // TreeItem of the tree in the code using the tree
11 |
12 | //A TreeItem is a single vertex in the tree. It contains a value and stores references to an arbitrary amount of child
13 | //TreeItems. To ensure that there cannot be a cycle in the tree, the AddChild method takes an instance of type T instead
14 | //of a TreeItem instance
15 | public class TreeItem
16 | {
17 | //Using a HashSet instead of a List is advantageous for this use case, as it usually doesn't take O(n) time to find
18 | //a single item. As every child is an independent object, it's unlikely that two different children come into
19 | //conflict (a set is characterized by only containing unique objects). Add and Remove operations on HashSets have
20 | //a time complexity of O(n), though most operations will take constant time, if the hashing algorithm used is
21 | //sufficiently evenly distributed (this should be the case with the implementation of the standard library)
22 | private readonly HashSet> children = new HashSet>();
23 |
24 | public TreeItem(T value) : this(value, null)
25 | { }
26 |
27 | //This constructor is declared as private to prevent users from creating cycles
28 | private TreeItem(T value, TreeItem parent)
29 | {
30 | this.Parent = parent;
31 | this.Value = value;
32 | }
33 |
34 | ///
35 | /// Adds a new TreeItem containing the provided value to the children of this TreeItem
36 | ///
37 | public void AddChild(T value)
38 | {
39 | //The AddChild operation could be implemented in O(1) time using a List, but the amortized time complexity
40 | //using .NET's HashSet should also be O(1)
41 | //again: to prevent cycles in the tree from happening, the user isn't allowed to pass
42 | //TreeItem instances directly
43 | this.children.Add(new TreeItem(value));
44 | }
45 |
46 | ///
47 | /// Removes a child from the tree if it is present
48 | ///
49 | public void RemoveChild(TreeItem child)
50 | {
51 | //The RemoveChild operation has a time complexity of O(n) regardless of if a List or HashSet is used, though
52 | //using .NET's HashSet it's likely to have a amortized time complexity of O(1)
53 | this.children.Remove(child);
54 | }
55 |
56 | ///
57 | /// Deletes this TreeItem from the tree
58 | ///
59 | public void Delete()
60 | {
61 | if(this.Parent != null)
62 | {
63 | this.Parent.RemoveChild(this);
64 | }
65 | }
66 |
67 | ///
68 | /// Returns a collection of all children of this TreeItem
69 | ///
70 | public IEnumerable> Children { get => this.children.AsEnumerable(); }
71 | ///
72 | /// Returns the Value stored in this TreeItem
73 | ///
74 | public T Value { get; set; }
75 | ///
76 | /// Returns the parent TreeItem of this TreeItem
77 | ///
78 | public TreeItem Parent { get; private set; }
79 | }
80 | }
--------------------------------------------------------------------------------
/Datastructures/Collections/LinkedList.cs:
--------------------------------------------------------------------------------
1 | namespace LunarDoggo.Datastructures.Collections
2 | {
3 | //A doubly linked list ist a data structure that is composed of single items that have a value and a reference to
4 | //the previous item and the next item in the list. The list itself only stores a reference to the the current head
5 | //(i.e. the first item) of the list.
6 | //If an item has to be inserted, a new item is created, linked and set as the new head. If one would want to insert
7 | //items at the end of the list, one would have to keep a reference to the tail of the list and add the new item after
8 | //that.
9 | //In practice array-based lists are preferred over linked lists in most cases, as following so many references is less
10 | //efficient than reading values from an array.
11 | //Linked lists don't need a delete operation, as the links only consist of references between items, if one would want
12 | //to remove an item from the list, one must only reassign the the next reference of the item's previous item and the
13 | //previous reference of the item's next item in O(1) time
14 | public class LinkedList where T : class //T must be a class because otherwise the "!=" operator isn't defined for generic T's
15 | {
16 | //The only thing to be done on initialization is to assign a new field of type LinkedListItem that represents the
17 | //head and is initialized with null
18 | private LinkedListItem head;
19 |
20 | ///
21 | /// Searches for the first item in the linked list that contains the provided and returns the
22 | /// list item if it exists
23 | ///
24 | public LinkedListItem Search(T value)
25 | {
26 | //The search operation has a time complexity of O(n) where n is the number of items in the list, as in the worst case
27 | //every item in the list has to be checked if it contains the searched value
28 |
29 | //If head is null, the loop won't run and null is returned. If the value isn't contained in the list, Search also returns
30 | //null after checking every single item. Otherwise there must be an item containing the searched value, that's found after
31 | //at most O(n) checks
32 | LinkedListItem current = head;
33 | while (current != null && current.Value != value)
34 | {
35 | current = current.Next;
36 | }
37 | return current;
38 | }
39 |
40 | ///
41 | /// Inserts a new list item at the beginning of the linked list
42 | ///
43 | public LinkedListItem Insert(T value)
44 | {
45 | //New items are inserted at the front of the list, therefore the previous reference of this.head has to be updated and
46 | //the new item has to be set as the new head
47 | LinkedListItem item = new LinkedListItem(value);
48 | item.Next = this.head;
49 | if (this.head != null)
50 | {
51 | this.head.Previous = item;
52 | }
53 | this.head = item;
54 | return item;
55 | }
56 | }
57 |
58 | public class LinkedListItem
59 | {
60 | public LinkedListItem(T value)
61 | {
62 | this.Value = value;
63 | }
64 |
65 | ///
66 | /// Gets or sets the reference to the item that's preceeding the current item in the linked list
67 | ///
68 | public LinkedListItem Previous { get; set; }
69 | ///
70 | /// Gets or sets the reference to item that's following the current item in the linked list
71 | ///
72 | public LinkedListItem Next { get; set; }
73 | ///
74 | /// Returns the value contained in the current list item
75 | ///
76 | public T Value { get; }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
14 |
15 |
16 |
17 |
18 |
20 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Datastructures/Graphs/Vertex.cs:
--------------------------------------------------------------------------------
1 | namespace LunarDoggo.Datastructures.Graphs
2 | {
3 | //This class is generic to allow for custom values in a vertex without needing to make a custom implementation of this class
4 | public class Vertex
5 | {
6 | //A HashSet facilitates checking if two vertices are adjacent, as hashing allows for accessing the information if
7 | //another vertex is contained in adjacent in O(1) time. Keep in mind though that there is no resolution mechanism
8 | //for hash collisions using this method. For objects like Instances of Vertex this usually doesn't do harm because
9 | //GetHashCode(), which is used to determine the hash is distinct between different instances of Vertex unless the
10 | //method is overridden
11 | private readonly HashSet> adjacent = new HashSet>();
12 |
13 | public Vertex(int id, T value)
14 | {
15 | this.Value = value;
16 | this.Id = id;
17 | }
18 |
19 | ///
20 | /// Returns the Id of this vertex in the that created it
21 | ///
22 | public int Id { get; }
23 | ///
24 | /// Returns or updates the value that's stored inside of this vertex
25 | ///
26 | public T Value { get; set; }
27 | ///
28 | /// Returns a collection of all Vertices adjacent to this vertex
29 | ///
30 | public IEnumerable> Adjacent { get => this.adjacent.AsEnumerable(); }
31 |
32 | ///
33 | /// Returns if there is an edge from this vertex to
34 | ///
35 | public bool IsAdjacent(Vertex other)
36 | {
37 | //this part doesn't require thread safety, as 1. the HashSet isn't modified by this check and
38 | //2. other threads could modify the adjacent-HashSet right after this check regardless
39 | return this.adjacent.Contains(other);
40 | }
41 |
42 | ///
43 | /// Adds a new edge from this vertex to to the graph; this method is
44 | /// marked as internal, because allowing arbitrary changes to edges from other sources than
45 | /// the graph itself will lead to inconsistent data
46 | ///
47 | internal void AddAdjacency(Vertex other)
48 | {
49 | //ensure thread safety when modifying adjacent
50 | lock (this.adjacent)
51 | {
52 | //The vertex is not concerned if the graph it's contained in is directed or undirected, as a result,
53 | //adjacencies of undirected graphs will have to be managed by the graph isntance itself
54 | //Additionally, using a HashSet has the advantage that one doesn't have to check if "other" actually
55 | //is contained in the HashSet before adding a new entry (https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.addview=net-6.0)
56 | this.adjacent.Add(other);
57 | }
58 | }
59 |
60 | ///
61 | /// Removes the edge from this vertex to from the graph; this method is
62 | /// marked as internal, because allowing arbitrary changes to edges from other sources than
63 | /// the graph itself will lead to inconsistent data
64 | ///
65 | internal void RemoveAdjacency(Vertex other)
66 | {
67 | //ensure thread safety when modifying adjacent
68 | lock (this.adjacent)
69 | {
70 | //The vertex is not concerned if the graph it's contained in is directed or undirected, as a result,
71 | //adjacencies of undirected graphs will have to be managed by the graph isntance itself
72 | //Additionally, using a HashSet has the advantage that one doesn't have to check if "other" actually
73 | //is contained in the HashSet before adding a new entry (https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.removeview=net-6.0)
74 | this.adjacent.Remove(other);
75 | }
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.Beginners.ConsoleIO/Program.cs:
--------------------------------------------------------------------------------
1 | //You can include namespaces with "using" in order to get access to the classes you need. using-directives always must be before any class declarations in your C#-file
2 | //The namespace "System" contains many things you could need, in this example we need "Console" from this namespace
3 | using System;
4 |
5 | namespace LunarDoggo.ConsoleIO
6 | {
7 | class Program
8 | {
9 | ///
10 | /// This is the entrypoint to your application. When you execute your application, this method is called with the parameters
11 | /// you specified in the application-call passed into "args". void just means, the Method doesn't return anything.
12 | /// static isn't important as of now, just know, that your Main-method must always be static
13 | ///
14 | static void Main(string[] args)
15 | {
16 | //Console.WriteLine() writes the provided string to the console-window and sets the cursor to the next line
17 | Console.WriteLine("Hi, I'm a simple console application written in C# that can prompt the user for input.");
18 | //Console.WriteLine() without any provided string just adds an empty line to the console output
19 | Console.WriteLine();
20 | //Console.Write writes the provided string to the console-window, the cursor will be on the same line
21 | Console.Write("Please tell me your name: ");
22 |
23 | /*
24 | * variables are declared in the format:
25 | * =
26 | * In this case we declare a new string-variable with the name "name" and assign the return value of Console.ReadLine() to it
27 | * Console.ReadLine() waits for the user to input characters to the console and returns these characters after the Return-key is pressed
28 | */
29 | string name = Console.ReadLine();
30 |
31 | Console.WriteLine();
32 |
33 | /*
34 | * if-statements allow you to execute a portion of your code only when a condition is met. In combination with "else" or "else if" you
35 | * can also branch your code into multiple execution-paths
36 | *
37 | * an if-statement follows the following format:
38 | * if() { }
39 | *
40 | * an else-if-statement follows a similar format and can be appended after the closing "}" of an if-statements body:
41 | * else if() { }
42 | *
43 | * an else-statement can be appended after an if- or else-if-statement, it doesn't have a condition and is executed, when none of the previous
44 | * statements-conditions are met. It follows the format:
45 | * else { }
46 | *
47 | * in this case the condition of the if-statement is String.IsNullOrEmpty(name) which checks if the value of name-variable consists of any
48 | * non-space-character and returns a false if this is the case, true is returned, when no non-space-chcaracters are contained or the strings length is 0
49 | */
50 | if (String.IsNullOrWhiteSpace(name))
51 | {
52 | Console.WriteLine("So you don't have a name? All right, then. Keep your secrets.");
53 | }
54 | else
55 | {
56 | //multiple trings can be concatenated into one by adding the "+"-operator between them
57 | //with the Trim()-method we remove all leading and trailing spaces in order to get a nice output without too many spaces between the text and the name
58 | Console.WriteLine("So your name is " + name.Trim() + ", nice to meet you. Have a great day :)");
59 | }
60 |
61 | //Console.ReadKey() waits for any key, that is not a control-key (Ctrl, Shift, Alt, ...), to be pressed and returns the pressed key (which we don't use in this example)
62 | Console.ReadKey();
63 | //after any key was pressed, the Main-Method is exited and the application terminated (= closed)
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CSharpBeginnerProjects
2 | In this repository you'll find some basic example projects showcasing some important features of C#, as well as some basic algorithms and datastructures that are a must know in the field of computer science.
3 |
4 | The project is provided _without_ any warranty under the Unilicense, so that the code can be used in any private or commercial project with or without modifications.
5 |
6 | # Example applications
7 | * [Console input output](https://github.com/lunardoggo/CSharpBeginnerProjects/tree/main/Projects/LunarDoggo.Beginners.ConsoleIO)
8 | * [Console input validation](https://github.com/lunardoggo/CSharpBeginnerProjects/tree/main/Projects/LunarDoggo.Beginners.ConsoleIOValidation)
9 | * [File system access](https://github.com/lunardoggo/CSharpBeginnerProjects/tree/main/Projects/LunarDoggo.FileSystemTree)
10 | * [Simple Quiz Game](https://github.com/lunardoggo/CSharpBeginnerProjects/tree/main/Projects/LunarDoggo.QuizGame)
11 | * [WPF TicTacToe](https://github.com/lunardoggo/CSharpBeginnerProjects/tree/main/Projects/LunarDoggo.TicTacToe) (Only works on Windows)
12 |
13 | ---
14 |
15 | The algurithms and data structures showcased in this project are intended to teach basic concepts, therefore they will largely be implemented in a way that they can easily be adapted to other languages. In order to understand the provided comments you also should take a look at [Big O notation](https://github.com/lunardoggo/CSharpBeginnerProjects/blob/main/Concepts/Big%20O%20notation.md)
16 |
17 | # Datastructures
18 | ### **Collections**
19 | * [LinkedList](https://github.com/lunardoggo/CSharpBeginnerProjects/blob/main/Datastructures/Collections/LinkedList.cs)
20 | * [ArrayList](https://github.com/lunardoggo/CSharpBeginnerProjects/blob/main/Datastructures/Collections/ArrayList.cs)
21 | * [Queue](https://github.com/lunardoggo/CSharpBeginnerProjects/blob/main/Datastructures/Collections/Queue.cs)
22 | * [PriorityQueue](https://github.com/lunardoggo/CSharpBeginnerProjects/)
23 | * [Stack](https://github.com/lunardoggo/CSharpBeginnerProjects/blob/main/Datastructures/Collections/Stack.cs)
24 | * [HashSet](https://github.com/lunardoggo/CSharpBeginnerProjects/)
25 |
26 | ### **Heaps**
27 | * [General Heap](https://github.com/lunardoggo/CSharpBeginnerProjects/)
28 | * [MinHeap](https://github.com/lunardoggo/CSharpBeginnerProjects/)
29 | * [MaxHeap](https://github.com/lunardoggo/CSharpBeginnerProjects/)
30 |
31 | ### **Trees**
32 | * [General Tree](https://github.com/lunardoggo/CSharpBeginnerProjects/)
33 | * [Binary Search Tree](https://github.com/lunardoggo/CSharpBeginnerProjects/)
34 |
35 | ### **Graphs**
36 | * [Undirected Graph](https://github.com/lunardoggo/CSharpBeginnerProjects/tree/main/Datastructures/Graphs)
37 | * [Directed Graph](https://github.com/lunardoggo/CSharpBeginnerProjects/tree/main/Datastructures/Graphs)
38 |
39 | ---
40 |
41 | # Algorithms
42 | ### **Sorting**
43 | * [InsertionSort](https://github.com/lunardoggo/CSharpBeginnerProjects/blob/main/Algorithms/Sorting/InsertionSort.cs)
44 | * [MergeSort](https://github.com/lunardoggo/CSharpBeginnerProjects/)
45 | * [HeapSort](https://github.com/lunardoggo/CSharpBeginnerProjects/)
46 | * [QuickSort](https://github.com/lunardoggo/CSharpBeginnerProjects/)
47 | * [RandomizedQuickSort](https://github.com/lunardoggo/CSharpBeginnerProjects/)
48 | * [CountingSort](https://github.com/lunardoggo/CSharpBeginnerProjects/)
49 | * [BucketSort](https://github.com/lunardoggo/CSharpBeginnerProjects/)
50 | * [RadixSort](https://github.com/lunardoggo/CSharpBeginnerProjects/)
51 |
52 | ### **Searching**
53 | * [BinarySearch](https://github.com/lunardoggo/CSharpBeginnerProjects/)
54 |
55 | ### **Pathfinding in Graphs**
56 | * [Breadth-First-Search/BFS](https://github.com/lunardoggo/CSharpBeginnerProjects/blob/main/Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs)
57 | * [Depth-First-Search/DFS](https://github.com/lunardoggo/CSharpBeginnerProjects/blob/main/Algorithms/Graphs/Pathfinding/DepthFirstSearch.cs)
58 | * [Dijkstra's algorithm](https://github.com/lunardoggo/CSharpBeginnerProjects/)
59 | * [A* algorithm](https://github.com/lunardoggo/CSharpBeginnerProjects/)
60 |
61 | ### **Minimum spanning trees and rooted spanning trees**
62 | * [Kruskal's algorithm](https://github.com/lunardoggo/CSharpBeginnerProjects/)
63 | * [Prim's algorithm](https://github.com/lunardoggo/CSharpBeginnerProjects/)
64 | * [Modified DFS for rooted minimum spanning trees](https://github.com/lunardoggo/CSharpBeginnerProjects/)
65 |
--------------------------------------------------------------------------------
/Datastructures/Collections/Queue.cs:
--------------------------------------------------------------------------------
1 | using LunarDoggo.Datastructures.Exceptions;
2 |
3 | namespace LunarDoggo.Datastructures.Collections
4 | {
5 | //A queue is a so called FIFO (first in, first out) data structure that works exactly as the name suggests:
6 | //items are added to queue and have to wait in line to be served. Items that are enqueued the earliest will
7 | //be removed the earliest. "headIndex" points to the element in the array that should be removed from the queue
8 | //on the next dequeue-call and "tailIndex" points to the index of the cell that should be filled by the next
9 | //enqueue-call. If "headIndex" and "tailIndex" are the same, the queue is considered empty.
10 | //The most primitive implementation uses an array that's not resized over the queue's lifetime. If the queue
11 | //runs full and another object is enqueued, an exception is thrown. Additionally, as the oldest items are
12 | //dequeued first, one must keep track of the current head and tail of the queue, as it will no longer be
13 | //guaranteed that the item at index 0 is the next item to be dequeued
14 | public class Queue
15 | {
16 | private readonly T[] cache;
17 | private int headIndex = 0;
18 | private int tailIndex = 0;
19 |
20 | public Queue(int capacity)
21 | {
22 | //The initialization of the queue has a time complexity of O(capacity), as C# not only allocates a certain memory
23 | //range, but also sets all cells of the array to the default value of type T
24 |
25 | //As the array will never be resized or reallocated, the capacity of the queue has to be provided on initialization
26 | this.cache = new T[capacity];
27 | }
28 |
29 | ///
30 | /// Adds a new item to the queue in last place
31 | ///
32 | ///
33 | public void Enqueue(T item)
34 | {
35 | //Enqueue has a time complexity of O(1), as only constant time operations are used sequentially
36 |
37 | //head points to the oldest element and tail to the next place to be used; if head and tail have the same
38 | //value after this operation, it cannot be distinguished between a full and an empty queue, therefore an
39 | //Exception must be thrown before adding the element to avoid this situation
40 | if ((this.tailIndex + 1) % this.cache.Length == this.headIndex)
41 | {
42 | throw new OverflowException("The queue is full");
43 | }
44 |
45 | this.cache[tailIndex] = item;
46 | //As head and tail can wrap around the end of the array, it must be ensured that the tail index never goes out of bounds
47 | this.tailIndex = (this.tailIndex + 1) % this.cache.Length;
48 | }
49 |
50 | ///
51 | /// Removes the first item of the queue and returns its value
52 | ///
53 | ///
54 | public T Dequeue()
55 | {
56 | //Dequeue has a time complexity of O(1), as only constant time operations are used sequentially
57 | if (this.IsEmpty)
58 | {
59 | throw new UnderflowException("The queue is empty");
60 | }
61 |
62 | T item = this.cache[this.headIndex];
63 | //As head and tail can wrap around the end of the array, it must be ensured that the head index never goes out of bounds
64 | this.headIndex = (this.headIndex + 1) % this.cache.Length;
65 | return item;
66 | }
67 |
68 | ///
69 | /// Returns the value of the first item in the queue
70 | ///
71 | public T Peek()
72 | {
73 | //Peek has a time complexity of O(1), as only constant time operations are used sequentially
74 |
75 | if (this.IsEmpty)
76 | {
77 | return default;
78 | }
79 | return this.cache[this.headIndex];
80 | }
81 |
82 | ///
83 | /// Returns whether the queue is empty
84 | ///
85 | public bool IsEmpty
86 | {
87 | //IsEmpty has a time complexity of O(1), as only constant time operations are used sequentially
88 | get => this.headIndex == this.tailIndex;
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Algorithms/Graphs/Pathfinding/DepthFirstSearch.cs:
--------------------------------------------------------------------------------
1 | using LunarDoggo.Datastructures.Graphs;
2 |
3 | namespace LunarDoggo.Algorithms.Graphs.Pathfinding
4 | {
5 | //DFS is a basic pathfinding algorithm that works on unweighted graphs. It doesn't matter if the graph is directed
6 | //or undirected, but a DFS run on a directed graph will yield a s-rooted spanning tree that isn't necessarily minimal
7 | //in weight if the graph contains at least one
8 | //The time complexity of DFS is O(|V| + |E|), as the algorithm processes every vertex and iterates over every edge exactly once.
9 | //In simple graphs |E| is bounded by O(|V|^2), so in such cases one could also use O(|V|^2) as the time complexity
10 | public class DepthFirstSearch
11 | {
12 | public void Run(IGraph graph)
13 | {
14 | if (graph is null)
15 | {
16 | throw new ArgumentNullException("The graph the DFS should be run on cannot be null");
17 | }
18 |
19 | this.Initialize(graph);
20 | //Current processing time
21 | int time = 0;
22 |
23 | //DFS is usually also implemented in a way that works on disconnected graphs by iterating over all vertices.
24 | //If a vertex hasn't been processed yet, it must be in a component of the graph that's unreachable from the
25 | //Previously processed part and therefore the recursion is started once again.
26 | foreach (Vertex vertex in graph.Vertices)
27 | {
28 | if (!vertex.Value!.Processed)
29 | {
30 | time = this.RunRecursively(graph, vertex, time);
31 | }
32 | }
33 | }
34 |
35 | private int RunRecursively(IGraph graph, Vertex start, int startTime)
36 | {
37 | //The difference between ++startTime and startTime++ is, that ++startTime will execute the increment before the assignment
38 | //and startTime++ afterwards. This shorthand assignment will be used here to shorten the code
39 | start.Value!.StartTime = startTime++;
40 |
41 | //DFS works iterative. One could picture it as an algorithm that walks as far as it can before turning back and processing
42 | //the adjacencies of previously processed vertices
43 | foreach (Vertex adjacent in start.Adjacent)
44 | {
45 | //Only vertices that haven't been processed yet need to be regarded further
46 | if (!adjacent.Value!.Processed)
47 | {
48 | adjacent.Value.Predecessor = start;
49 | this.RunRecursively(graph, adjacent, startTime);
50 | }
51 | }
52 |
53 | //After setting the end time the processing of the vertex is finished
54 | start.Value!.EndTime = startTime++;
55 | return startTime;
56 | }
57 |
58 | private void Initialize(IGraph graph)
59 | {
60 | foreach (Vertex vertex in graph.Vertices)
61 | {
62 | //Make sure that there is no vertex that doesn't have any data attached to it; this is not part of
63 | //the original algorithm
64 | if (vertex.Value is null)
65 | {
66 | vertex.Value = new DFSVertex();
67 | }
68 |
69 | //The original algorithm inizializes every vertex with invalid start and end times, as well as a null predecessor
70 | //Instead of the processed flag, some implementations use colors to facilitate determining the time complexity of
71 | vertex.Value.StartTime = -1;
72 | vertex.Value.EndTime = -1;
73 | vertex.Value.Predecessor = null;
74 | }
75 | }
76 | }
77 |
78 | public class DFSVertex
79 | {
80 | ///
81 | /// Returns whether this vertex has already been processed. It is considered processed if the start time is valid
82 | ///
83 | public bool Processed { get => this.StartTime != -1; }
84 | ///
85 | /// Returns the predecessor of this vertex on the path from the previous vertex
86 | ///
87 | public Vertex Predecessor { get; set; }
88 | ///
89 | /// Returns the start time of the processing of this vertex
90 | ///
91 | public int StartTime { get; set; }
92 | ///
93 | /// Returns the end time of the processing of this vertex
94 | ///
95 | public int EndTime { get; set; }
96 | }
97 | }
--------------------------------------------------------------------------------
/LunarDoggo.CSharpBeginnerProjects.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.7.34202.233
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LunarDoggo.ConsoleIO", "Projects\LunarDoggo.Beginners.ConsoleIO\LunarDoggo.ConsoleIO.csproj", "{897DEAB7-A5F6-4030-89D8-0DE3AA6A71A3}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0C6CDEED-71CC-4A40-8C27-7E267EFAAC88}"
9 | ProjectSection(SolutionItems) = preProject
10 | .editorconfig = .editorconfig
11 | .gitignore = .gitignore
12 | LICENSE = LICENSE
13 | README.md = README.md
14 | EndProjectSection
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LunarDoggo.ConsoleIOValidation", "Projects\LunarDoggo.Beginners.ConsoleIOValidation\LunarDoggo.ConsoleIOValidation.csproj", "{A8D83E8B-A5D8-4B74-AF98-46C1B458CAD2}"
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LunarDoggo.FileSystemTree", "Projects\LunarDoggo.FileSystemTree\LunarDoggo.FileSystemTree.csproj", "{883884B1-0791-4C9F-B497-543FD7D7C228}"
19 | EndProject
20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LunarDoggo.QuizGame", "Projects\LunarDoggo.QuizGame\LunarDoggo.QuizGame.csproj", "{B95D0A2F-14F5-482A-8640-56B84AB570D3}"
21 | EndProject
22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LunarDoggo.TicTacToe", "Projects\LunarDoggo.TicTacToe\LunarDoggo.TicTacToe.csproj", "{6E55DCAE-9F1F-4438-843C-36A8C436FEE7}"
23 | EndProject
24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LunarDoggo.Datastructures", "Datastructures\LunarDoggo.Datastructures.csproj", "{C427DCA1-9268-481F-B9C9-21A5AB1458D4}"
25 | EndProject
26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LunarDoggo.Algorithms", "Algorithms\LunarDoggo.Algorithms.csproj", "{AA6715E0-5EA1-4D3A-A8D9-CCBA35B43965}"
27 | EndProject
28 | Global
29 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
30 | Debug|Any CPU = Debug|Any CPU
31 | Release|Any CPU = Release|Any CPU
32 | EndGlobalSection
33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
34 | {897DEAB7-A5F6-4030-89D8-0DE3AA6A71A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {897DEAB7-A5F6-4030-89D8-0DE3AA6A71A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {897DEAB7-A5F6-4030-89D8-0DE3AA6A71A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {897DEAB7-A5F6-4030-89D8-0DE3AA6A71A3}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {A8D83E8B-A5D8-4B74-AF98-46C1B458CAD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {A8D83E8B-A5D8-4B74-AF98-46C1B458CAD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {A8D83E8B-A5D8-4B74-AF98-46C1B458CAD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {A8D83E8B-A5D8-4B74-AF98-46C1B458CAD2}.Release|Any CPU.Build.0 = Release|Any CPU
42 | {883884B1-0791-4C9F-B497-543FD7D7C228}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43 | {883884B1-0791-4C9F-B497-543FD7D7C228}.Debug|Any CPU.Build.0 = Debug|Any CPU
44 | {883884B1-0791-4C9F-B497-543FD7D7C228}.Release|Any CPU.ActiveCfg = Release|Any CPU
45 | {883884B1-0791-4C9F-B497-543FD7D7C228}.Release|Any CPU.Build.0 = Release|Any CPU
46 | {B95D0A2F-14F5-482A-8640-56B84AB570D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47 | {B95D0A2F-14F5-482A-8640-56B84AB570D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
48 | {B95D0A2F-14F5-482A-8640-56B84AB570D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
49 | {B95D0A2F-14F5-482A-8640-56B84AB570D3}.Release|Any CPU.Build.0 = Release|Any CPU
50 | {6E55DCAE-9F1F-4438-843C-36A8C436FEE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51 | {6E55DCAE-9F1F-4438-843C-36A8C436FEE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
52 | {6E55DCAE-9F1F-4438-843C-36A8C436FEE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
53 | {6E55DCAE-9F1F-4438-843C-36A8C436FEE7}.Release|Any CPU.Build.0 = Release|Any CPU
54 | {C427DCA1-9268-481F-B9C9-21A5AB1458D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55 | {C427DCA1-9268-481F-B9C9-21A5AB1458D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
56 | {C427DCA1-9268-481F-B9C9-21A5AB1458D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
57 | {C427DCA1-9268-481F-B9C9-21A5AB1458D4}.Release|Any CPU.Build.0 = Release|Any CPU
58 | {AA6715E0-5EA1-4D3A-A8D9-CCBA35B43965}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
59 | {AA6715E0-5EA1-4D3A-A8D9-CCBA35B43965}.Debug|Any CPU.Build.0 = Debug|Any CPU
60 | {AA6715E0-5EA1-4D3A-A8D9-CCBA35B43965}.Release|Any CPU.ActiveCfg = Release|Any CPU
61 | {AA6715E0-5EA1-4D3A-A8D9-CCBA35B43965}.Release|Any CPU.Build.0 = Release|Any CPU
62 | EndGlobalSection
63 | GlobalSection(SolutionProperties) = preSolution
64 | HideSolutionNode = FALSE
65 | EndGlobalSection
66 | GlobalSection(ExtensibilityGlobals) = postSolution
67 | SolutionGuid = {D15F71B6-C1E2-4512-A5CA-C5E2D0196A43}
68 | EndGlobalSection
69 | EndGlobal
70 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/LunarDoggo.TicTacToe.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {6E55DCAE-9F1F-4438-843C-36A8C436FEE7}
8 | WinExe
9 | TicTacToe
10 | TicTacToe
11 | v4.5.2
12 | 512
13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 4
15 | true
16 | true
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | 4.0
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | MSBuild:Compile
56 | Designer
57 |
58 |
59 | MSBuild:Compile
60 | Designer
61 |
62 |
63 | App.xaml
64 | Code
65 |
66 |
67 |
68 |
69 |
70 | MainWindow.xaml
71 | Code
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | Code
80 |
81 |
82 | True
83 | True
84 | Resources.resx
85 |
86 |
87 | True
88 | Settings.settings
89 | True
90 |
91 |
92 | ResXFileCodeGenerator
93 | Resources.Designer.cs
94 |
95 |
96 | SettingsSingleFileGenerator
97 | Settings.Designer.cs
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.QuizGame/GameLoop.cs:
--------------------------------------------------------------------------------
1 | using LunarDoggo.QuizGame.Visuals;
2 | using System.Collections.Generic;
3 | using System;
4 |
5 | namespace LunarDoggo.QuizGame
6 | {
7 | ///
8 | /// This class contains the logic to run the game
9 | ///
10 | public class GameLoop
11 | {
12 | private readonly IVisualizer visualizer; //swappable IVisualizer-instance due to interface-usage
13 | private readonly GameState state;
14 |
15 | private bool isStarted; //Indicator if the game has started yet
16 |
17 | public bool IsFinished { get; private set; }
18 |
19 | ///
20 | /// Creates a new instance of with the provided -instance and
21 | /// provided s
22 | ///
23 | public GameLoop(IVisualizer visualizer, IEnumerable questions)
24 | {
25 | this.state = new GameState(questions);
26 | this.visualizer = visualizer;
27 | }
28 |
29 | ///
30 | /// Runs one cycle of the game (rendering, input processing)
31 | ///
32 | public void DoTick()
33 | {
34 | //the is finished-flag is stored in this local variable and set after all other game loop related
35 | //tasks have finished because otherwise the status (correct, wrong) of the last answer of the game
36 | //isn't displayed to the user because the game is finished before the cycle, the status would be printed
37 | bool finished = !this.state.HasUnansweredQuestions;
38 | //First draw the content to the screen, afterwards process the input
39 | this.UpdateScreen();
40 | this.ProcessInput();
41 | this.IsFinished = finished;
42 | }
43 |
44 | ///
45 | /// Queries and processes the current input
46 | ///
47 | private void ProcessInput()
48 | {
49 | ConsoleKeyInfo key = Console.ReadKey();
50 |
51 | switch (key.Key)
52 | {
53 | case ConsoleKey.UpArrow:
54 | this.ChangeHighlightedAnswer(true);
55 | break;
56 | case ConsoleKey.DownArrow:
57 | this.ChangeHighlightedAnswer(false);
58 | break;
59 | case ConsoleKey.Enter:
60 | this.ProcessEnterPress();
61 | break;
62 | }
63 | }
64 |
65 | ///
66 | /// Updates the screen according to the current game state
67 | ///
68 | private void UpdateScreen()
69 | {
70 | if (!this.isStarted)
71 | {
72 | this.visualizer.DrawGameStart(this.state.TotalQuestionCount);
73 | }
74 |
75 | QuizQuestion question = this.state.CurrentQuestion;
76 | if (question != null)
77 | {
78 | this.visualizer.DrawQuizQuestion(question, this.state.HighlightedAnswer.Id);
79 | }
80 | if (this.state.IsCurrentQuestionAnswered)
81 | {
82 | this.visualizer.DrawAnswerStatus(this.state.ChosenAnswer.IsCorrect, question.CorrectAnswer);
83 | }
84 |
85 | if (!this.state.HasUnansweredQuestions)
86 | {
87 | this.visualizer.DrawGameResult(this.state.TotalQuestionCount, this.state.CorrectAnswersCount);
88 | }
89 | }
90 |
91 | ///
92 | /// Moves the cursor up or down to highlight the previous or next question respectively
93 | ///
94 | /// flag whether the cursor should be moved upwards to the previous answer
95 | private void ChangeHighlightedAnswer(bool up)
96 | {
97 | if (this.state.CurrentQuestion != null && !this.state.IsCurrentQuestionAnswered)
98 | {
99 | if (up)
100 | {
101 | this.state.HighlightPreviousAnswer();
102 | }
103 | else
104 | {
105 | this.state.HighlightNextAnswer();
106 | }
107 | }
108 | }
109 |
110 | ///
111 | /// Takes the suitable action according to the game state if the enter-key was pressed
112 | ///
113 | private void ProcessEnterPress()
114 | {
115 | if (this.isStarted)
116 | {
117 | if (this.state.IsCurrentQuestionAnswered)
118 | {
119 | this.state.MoveToNextQuestion();
120 | }
121 | else
122 | {
123 | this.state.AnswerQuestion();
124 | }
125 | }
126 | else
127 | {
128 | this.isStarted = true;
129 | this.state.MoveToNextQuestion();
130 | }
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/GameBoard.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TicTacToe
4 | {
5 | ///
6 | /// Represents a consisting of 9 tiles separated into three rows and columns
7 | ///
8 | public class GameBoard
9 | {
10 | ///
11 | /// Raised when a player occupied an unoccupied tile
12 | ///
13 | public event EventHandler PlayerOccupiedTile;
14 |
15 | //this is a multi-dimensional-array, the amount of "," between [] specifies how many dimensions
16 | //the array has: (dimensioncount) n = (commacount) c + 1
17 | private readonly byte[,] tiles = new byte[3, 3];
18 |
19 | ///
20 | /// Tries to occupy the specified tile for the specified player and returns whether it was successful
21 | ///
22 | public bool OccupyTile(Player player, int x, int y)
23 | {
24 | if (this.IsTileOccupied(x, y))
25 | {
26 | return false;
27 | }
28 | //multidimensional arrays are accessed like 1D-arrays, just with more parameters between the []
29 | //you can picture the accessor in this case as this.tiles[row, column]
30 | this.tiles[x, y] = player.Id;
31 |
32 | this.PlayerOccupiedTile?.Invoke(this, new PlayerTileEventArgs(player, x, y));
33 | return true;
34 | }
35 |
36 | ///
37 | /// Returns whether all tiles are occupied by any player
38 | ///
39 | public bool AreAllFieldsOccupied()
40 | {
41 | //with tiles.GetLength(0) we get the length of the first dimension of the tiles-2D-array
42 | for (int x = 0; x < tiles.GetLength(0); x++)
43 | {
44 | //with tiles.GetLength(1) we get the length of the second dimension of the tiles-2D-array
45 | for (int y = 0; y < tiles.GetLength(1); y++)
46 | {
47 | if (!this.IsTileOccupied(x, y))
48 | {
49 | return false;
50 | }
51 | }
52 | }
53 | return true;
54 | }
55 |
56 | ///
57 | /// Returns whether the tile with the specified position is already occupied by any player
58 | ///
59 | private bool IsTileOccupied(int x, int y)
60 | {
61 | return this.tiles[x, y] != 0;
62 | }
63 |
64 | ///
65 | /// Returns whether the specified player occupies three consecutive tiles in any position and therefore has won the game
66 | ///
67 | public bool HasWon(byte player)
68 | {
69 | for (int i = 0; i < this.tiles.GetLength(0); i++)
70 | {
71 | if (this.HasWonRow(player, i) || this.HasWonColumn(player, i))
72 | {
73 | return true;
74 | }
75 | }
76 | return this.HasWonDiagonals(player);
77 | }
78 |
79 | ///
80 | /// Returns whether the specified player occupies all three tiles of a row
81 | ///
82 | private bool HasWonRow(byte player, int rowIndex)
83 | {
84 | //the & operator is the binary-and-operator
85 | //here we use it to make the check if the player has occupied all tiles of the row a bit shorter
86 | //if the tiles of the row are occupied by: 1 0 1 (0 = none, 1 = player one), and the provided player is 1,
87 | //this will lead to (1 == (1 & 0 & 1)), which will be (1 == 0) which is false
88 | //if the tiles are occupied by: 1 1 1, the result will be: (1 == (1 & 1 & 1)) and therefore (1 == 1) which is true
89 | return player == (this.tiles[rowIndex, 0] & this.tiles[rowIndex, 1] & this.tiles[rowIndex, 2]);
90 | }
91 |
92 | ///
93 | /// Returns whether the specified player occupies all three tiles of a column
94 | ///
95 | private bool HasWonColumn(byte player, int columnIndex)
96 | {
97 | return player == (this.tiles[0, columnIndex] & this.tiles[1, columnIndex] & this.tiles[2, columnIndex]);
98 | }
99 |
100 | ///
101 | /// Returns whether the specified player won any of the two diagonals of the
102 | ///
103 | private bool HasWonDiagonals(byte player)
104 | {
105 | return player == (this.tiles[0, 0] & this.tiles[1, 1] & this.tiles[2, 2])
106 | || player == (this.tiles[0, 2] & this.tiles[1, 1] & this.tiles[2, 0]);
107 | }
108 |
109 | ///
110 | /// Resets the tiles of the to be unoccupied
111 | ///
112 | public void Reset()
113 | {
114 | for (int x = 0; x < tiles.GetLength(0); x++)
115 | {
116 | for (int y = 0; y < tiles.GetLength(1); y++)
117 | {
118 | this.tiles[x, y] = 0;
119 | }
120 | }
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/GameState.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System;
3 |
4 | namespace TicTacToe
5 | {
6 | ///
7 | /// Represents the current state of the game
8 | ///
9 | public class GameState
10 | {
11 | //EventHandlers always use the syntyx event EventHandler
12 | //the event operator makes sure, that a class can only handle its own subscriptions to the event and
13 | //can't interfere with the subscriptions of other classes
14 |
15 | ///
16 | /// Raised when a player won the game
17 | ///
18 | public event EventHandler GameOverPlayerWon;
19 | ///
20 | /// Raised when a player tried to occupy an already occupied tile
21 | ///
22 | public event EventHandler TileAlreadyOccupied;
23 | ///
24 | /// Raised when the game ended in a draw
25 | ///
26 | public event EventHandler GameOverDraw;
27 | ///
28 | /// Raised when all tiles were reset
29 | ///
30 | public event EventHandler TilesReset;
31 |
32 | private readonly ButtonTileMapping[] buttonTileMappings;
33 | private readonly GameBoard gameBoard;
34 | private readonly Player secondPlayer;
35 | private readonly Player firstPlayer;
36 |
37 | //Varialbe to switch between the two players
38 | private Player currentPlayer;
39 |
40 | public GameState(ButtonTileMapping[] buttonTileMappings)
41 | {
42 | this.buttonTileMappings = buttonTileMappings;
43 | //The first player will be displayed as "X" on the tiles, the second as "O"
44 | this.firstPlayer = new Player(1, "X");
45 | this.secondPlayer = new Player(2, "O");
46 | this.gameBoard = new GameBoard();
47 | this.gameBoard.PlayerOccupiedTile += this.OnPlayerOccupiedTile;
48 | }
49 |
50 | ///
51 | /// Handles
52 | ///
53 | private void OnPlayerOccupiedTile(object sender, PlayerTileEventArgs e)
54 | {
55 | //We just set the Content-display of the button of the tile to the current players displayname
56 | this.buttonTileMappings.Single(_mapping => _mapping.TileX == e.TileX && _mapping.TileY == e.TileY).Button.Content = e.Player.Display;
57 | }
58 |
59 | ///
60 | /// Resets all tiles and sets the current player to the first one
61 | ///
62 | public void StartGame()
63 | {
64 | this.currentPlayer = this.firstPlayer;
65 | this.gameBoard.Reset();
66 | this.TilesReset?.Invoke(this, EventArgs.Empty);
67 | }
68 |
69 | ///
70 | /// Tells the to occupy a specific tile for the
71 | ///
72 | ///
73 | public void OccupyTile(ButtonTileMapping mapping)
74 | {
75 | if (this.gameBoard.OccupyTile(this.currentPlayer, mapping.TileX, mapping.TileY))
76 | {
77 | //If the tile of the gameboard can be occupied, check if the game is either won
78 | //or ended in a draw. If neither is the case, switch the player
79 | if (this.gameBoard.HasWon(this.currentPlayer.Id))
80 | {
81 | //Events could be raised by (in this case) calling this.GameOverPlayerWon(this, new PlayerEventArgs(this.currentPlayer))
82 | //But if there aren't any handlers subscribed to it, this will lead to a NullReferenceException
83 | //To prevent this, use the null-conditional-operator with a call to the Invoke-Method for the event
84 | //this is equivalent to:
85 | // if(this.GameOverPlayerWon != null) { this.GameOverPlayerWon(this, new PlayerEventArgs(this.currentPlayer)); }
86 | this.GameOverPlayerWon?.Invoke(this, new PlayerEventArgs(this.currentPlayer));
87 | }
88 | else if (this.gameBoard.AreAllFieldsOccupied())
89 | {
90 | //If you want to raise an event, that uses the regular EventArgs as an eventtype,
91 | //you can use EventArgs.Empty instead of new EventArgs()
92 | this.GameOverDraw?.Invoke(this, EventArgs.Empty);
93 | }
94 | else
95 | {
96 | this.SwitchPlayer();
97 | }
98 | }
99 | else
100 | {
101 | //If the tile can't be occupied, it must already be occupied by either player
102 | this.TileAlreadyOccupied?.Invoke(this, EventArgs.Empty);
103 | }
104 | }
105 |
106 | ///
107 | /// Switches between player one and two
108 | ///
109 | private void SwitchPlayer()
110 | {
111 | if (this.currentPlayer == this.firstPlayer)
112 | {
113 | this.currentPlayer = this.secondPlayer;
114 | }
115 | else
116 | {
117 | this.currentPlayer = this.firstPlayer;
118 | }
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.Beginners.ConsoleIOValidation/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System;
3 |
4 | namespace LunarDoggo.ConsoleIOValidation
5 | {
6 | class Program
7 | {
8 | /*
9 | * you can declare and initialize variables outside of methods. Note, that a variable must be static
10 | * if you want to use it in a static method
11 | * a List is a collection of objects, in this case strings
12 | * if a variable is readonly, it can only be assigned in a constructor or directly like here
13 | * The type "List" is contained inside of the namespace "System.Collections.Generic"
14 | */
15 | private readonly static List games = new List();
16 |
17 | private static void Main(string[] args)
18 | {
19 | //Similarily to variables, you can also call your methods by their names. Program just provides
20 | //further information for you, where to find the method
21 | Program.AddGames();
22 |
23 | Program.PrintGameSelection();
24 |
25 | //the output of a method can directly be assigned to a variable
26 | int gameIndex = Program.GetSelectedGame();
27 |
28 | //we get the value at the users selected index by using the indexer of our list:
29 | //Program.games[index]; note that an IndexOutOfRangeException is thrown if you try to access an invalid index (eg. less than 0)
30 | string gameName = Program.games[gameIndex];
31 |
32 | //parameters are input in the same order as they are declared in the method-signature
33 | Program.PrintGameChoice(gameName);
34 |
35 | Console.ReadKey();
36 | }
37 |
38 | /*
39 | * you can add your custom static methods by using the format:
40 | * [visibility-modifier] static ([parameters])
41 | * this method is defined as private and therefore can only be accessed inside of the class Program, empty
42 | * parenthesis mean, that the method doesn't take any parameters
43 | */
44 | private static void AddGames()
45 | {
46 | /*
47 | * static variables, that are declared outside of any method can be accessed by writing their name. It is advisable
48 | * that you write the class-name before the variable-name in order to give you more information about its context.
49 | * here we just add some entries to the games-dictionary
50 | */
51 | Program.games.Add("The Wither 3 - Wild Hunt");
52 | Program.games.Add("Baba is you");
53 | Program.games.Add("Factorio");
54 | Program.games.Add("Cities Skylines");
55 | Program.games.Add("Kerbal space program");
56 | }
57 |
58 | private static void PrintGameSelection()
59 | {
60 | /*
61 | * a for-loop executes the code inside of its body as many times as the loop-condition is met and can be defined with the following format:
62 | * for(; ; ) { }
63 | *
64 | * In this case we declare a new integer-variable with the name "i" and assign the value "0" to it. For the condition we check if the
65 | * coutner-variable is less than the current count of games in our previously defined list of games. If the condition is not met,
66 | * the counter-variable is incremented by one (i++) otherwise the loop is exited
67 | */
68 | for (int i = 0; i < Program.games.Count; i++)
69 | {
70 | //We just print the index of the game and the name of it to the console
71 | Console.WriteLine("[" + i + "]: " + Program.games[i]);
72 | }
73 | }
74 |
75 | //If your method should return a value, just add the name of the return-type instead of void to the signature
76 | private static int GetSelectedGame()
77 | {
78 | //variables can be declared without assigning a value to it, this should only be done if you assign a value shortly afterwards
79 | int chosenIndex;
80 | string choice;
81 |
82 | //a do-while-loop first enters its body, executes the contained code and afterwards checks the condition
83 | do
84 | {
85 | //Note that in C# collections (array, list, dictionary, ...) start at the index 0, therefore the last index
86 | //is the length of the collection minus one
87 | Console.Write("Please select a game by it's index-number (between 0 and " + (Program.games.Count - 1) + "): ");
88 | choice = Console.ReadLine();
89 | //Int32.TryParse is a method to check if a string is a number and if this is the case to put out the parsed value to the out-parameter-variable
90 | //similar methods exist for double, bool, DateTime, and many others
91 | //in order to prevent errors further on, we also check if the chosen index is a valid index of our list
92 | //with the "!"-operator, the whole boolean-expression inside the parenthesis is negated
93 | } while (!(Int32.TryParse(choice, out chosenIndex) && chosenIndex >= 0 && chosenIndex < Program.games.Count));
94 |
95 | //the return-value of your custom method is returned via a return-statement:
96 | //return ;
97 | return chosenIndex;
98 | }
99 |
100 | //If you want to add paramters to your methods, use the format " " for each parameter,
101 | //if you want to add multiple parameters, separate them with a colon
102 | private static void PrintGameChoice(string gameName)
103 | {
104 | Console.WriteLine();
105 | //As you normally cant directly write doublequotes into C#-strings, we use the escape-character backslash "\",
106 | //which tells the compiler, that the following character is to be literally inserted into the string.
107 | Console.WriteLine("So you have chosen \"" + gameName + "\". GLHF :)");
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | dotnet_diagnostic.CS8600.severity = silent
4 | dotnet_diagnostic.CS8602.severity = silent
5 | dotnet_diagnostic.CS8603.severity = silent
6 | dotnet_diagnostic.CS8618.severity = silent
7 | dotnet_diagnostic.CS8601.severity = silent
8 | dotnet_diagnostic.CS8625.severity = silent;
9 |
10 | [*.cs]
11 | #### Benennungsstile ####
12 |
13 | # Benennungsregeln
14 |
15 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
16 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
17 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
18 |
19 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
20 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
21 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
22 |
23 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
24 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
25 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
26 |
27 | # Symbolspezifikationen
28 |
29 | dotnet_naming_symbols.interface.applicable_kinds = interface
30 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
31 | dotnet_naming_symbols.interface.required_modifiers =
32 |
33 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
34 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
35 | dotnet_naming_symbols.types.required_modifiers =
36 |
37 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
38 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
39 | dotnet_naming_symbols.non_field_members.required_modifiers =
40 |
41 | # Benennungsstile
42 |
43 | dotnet_naming_style.begins_with_i.required_prefix = I
44 | dotnet_naming_style.begins_with_i.required_suffix =
45 | dotnet_naming_style.begins_with_i.word_separator =
46 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
47 |
48 | dotnet_naming_style.pascal_case.required_prefix =
49 | dotnet_naming_style.pascal_case.required_suffix =
50 | dotnet_naming_style.pascal_case.word_separator =
51 | dotnet_naming_style.pascal_case.capitalization = pascal_case
52 |
53 | dotnet_naming_style.pascal_case.required_prefix =
54 | dotnet_naming_style.pascal_case.required_suffix =
55 | dotnet_naming_style.pascal_case.word_separator =
56 | dotnet_naming_style.pascal_case.capitalization = pascal_case
57 | csharp_space_around_binary_operators = before_and_after
58 | csharp_style_expression_bodied_methods = false:silent
59 | csharp_style_expression_bodied_constructors = false:silent
60 | csharp_style_expression_bodied_operators = false:silent
61 | csharp_style_expression_bodied_properties = true:silent
62 | csharp_style_expression_bodied_indexers = true:silent
63 | csharp_style_expression_bodied_accessors = true:silent
64 | csharp_style_expression_bodied_lambdas = true:silent
65 | csharp_style_expression_bodied_local_functions = false:silent
66 |
67 | [*.vb]
68 | #### Benennungsstile ####
69 |
70 | # Benennungsregeln
71 |
72 | dotnet_naming_rule.interface_should_be_beginnt_mit_i.severity = suggestion
73 | dotnet_naming_rule.interface_should_be_beginnt_mit_i.symbols = interface
74 | dotnet_naming_rule.interface_should_be_beginnt_mit_i.style = beginnt_mit_i
75 |
76 | dotnet_naming_rule.typen_should_be_pascal_schreibweise.severity = suggestion
77 | dotnet_naming_rule.typen_should_be_pascal_schreibweise.symbols = typen
78 | dotnet_naming_rule.typen_should_be_pascal_schreibweise.style = pascal_schreibweise
79 |
80 | dotnet_naming_rule.nicht_feldmember_should_be_pascal_schreibweise.severity = suggestion
81 | dotnet_naming_rule.nicht_feldmember_should_be_pascal_schreibweise.symbols = nicht_feldmember
82 | dotnet_naming_rule.nicht_feldmember_should_be_pascal_schreibweise.style = pascal_schreibweise
83 |
84 | # Symbolspezifikationen
85 |
86 | dotnet_naming_symbols.interface.applicable_kinds = interface
87 | dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
88 | dotnet_naming_symbols.interface.required_modifiers =
89 |
90 | dotnet_naming_symbols.typen.applicable_kinds = class, struct, interface, enum
91 | dotnet_naming_symbols.typen.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
92 | dotnet_naming_symbols.typen.required_modifiers =
93 |
94 | dotnet_naming_symbols.nicht_feldmember.applicable_kinds = property, event, method
95 | dotnet_naming_symbols.nicht_feldmember.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
96 | dotnet_naming_symbols.nicht_feldmember.required_modifiers =
97 |
98 | # Benennungsstile
99 |
100 | dotnet_naming_style.beginnt_mit_i.required_prefix = I
101 | dotnet_naming_style.beginnt_mit_i.required_suffix =
102 | dotnet_naming_style.beginnt_mit_i.word_separator =
103 | dotnet_naming_style.beginnt_mit_i.capitalization = pascal_case
104 |
105 | dotnet_naming_style.pascal_schreibweise.required_prefix =
106 | dotnet_naming_style.pascal_schreibweise.required_suffix =
107 | dotnet_naming_style.pascal_schreibweise.word_separator =
108 | dotnet_naming_style.pascal_schreibweise.capitalization = pascal_case
109 |
110 | dotnet_naming_style.pascal_schreibweise.required_prefix =
111 | dotnet_naming_style.pascal_schreibweise.required_suffix =
112 | dotnet_naming_style.pascal_schreibweise.word_separator =
113 | dotnet_naming_style.pascal_schreibweise.capitalization = pascal_case
114 |
115 | [*.{cs,vb}]
116 | dotnet_style_operator_placement_when_wrapping = beginning_of_line
117 | dotnet_style_coalesce_expression = true:suggestion
118 | dotnet_style_null_propagation = true:suggestion
119 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
120 | dotnet_style_prefer_auto_properties = true:silent
121 | dotnet_style_object_initializer = true:suggestion
122 | dotnet_style_collection_initializer = true:suggestion
123 | dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
124 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent
125 | dotnet_style_prefer_conditional_expression_over_return = true:silent
126 | dotnet_style_explicit_tuple_names = true:suggestion
127 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
128 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
129 | dotnet_style_prefer_compound_assignment = true:suggestion
130 | dotnet_style_prefer_simplified_interpolation = true:suggestion
131 | dotnet_style_namespace_match_folder = true:suggestion
--------------------------------------------------------------------------------
/Projects/LunarDoggo.TicTacToe/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 | using System.Windows;
3 | using System.Linq;
4 | using System;
5 |
6 | namespace TicTacToe
7 | {
8 | ///
9 | /// Interactionlogic for MainWindow.xaml
10 | ///
11 | public partial class MainWindow : Window
12 | {
13 | private readonly ButtonTileMapping[] buttonTileMappings;
14 | private readonly GameState gameState;
15 |
16 | public MainWindow()
17 | {
18 | InitializeComponent();
19 |
20 | this.buttonTileMappings = this.GetButtonTileMappings();
21 |
22 | this.gameState = new GameState(this.buttonTileMappings);
23 | //With the += operator you can subscribe to events, with -= you can unsubscribe
24 | //Make sure to unsubscribe from all events, when your object is no longer used in order to prevent "memory leaks"
25 | //because the garbage collector won't free the memory of the object because the event source still has a reference to it
26 | //In this case we don't need to unsubscribe any events because we never dispose of the GameState or GameBoard until the
27 | //application is terminated and therefore all resources are released
28 | this.gameState.TileAlreadyOccupied += this.OnTileAlreadyOccupied;
29 | this.gameState.GameOverPlayerWon += this.OnPlayerWon;
30 | this.gameState.GameOverDraw += this.OnGameOverDraw;
31 | this.gameState.TilesReset += this.OnTilesReset;
32 | this.gameState.StartGame();
33 |
34 | }
35 |
36 | ///
37 | /// Returns the mappings between the s of the UI and the tiles of the
38 | ///
39 | private ButtonTileMapping[] GetButtonTileMappings()
40 | {
41 | return new ButtonTileMapping[]
42 | {
43 | new ButtonTileMapping(this.btnTopLeft, 0, 0),
44 | new ButtonTileMapping(this.btnTopCenter, 1, 0),
45 | new ButtonTileMapping(this.btnTopRight, 2, 0),
46 | new ButtonTileMapping(this.btnMiddleLeft, 0, 1),
47 | new ButtonTileMapping(this.btnMiddleCenter, 1, 1),
48 | new ButtonTileMapping(this.btnMiddleRight, 2, 1),
49 | new ButtonTileMapping(this.btnBottomLeft, 0, 2),
50 | new ButtonTileMapping(this.btnBottomCenter, 1, 2),
51 | new ButtonTileMapping(this.btnBottomRight, 2, 2),
52 | };
53 | }
54 |
55 | //Event handlers always use the signature void (object sender, e)
56 | ///
57 | /// Handles the event and displays a message that the user can't occupy the clicked tile
58 | ///
59 | private void OnTileAlreadyOccupied(object sender, EventArgs e)
60 | {
61 | //This shows a default windows messagebox, you have to provide at least the first parameter
62 | //Parameter 1 (required): displayed message
63 | //Parameter 2 (optional): caption/title of the MessageBox
64 | //Parameter 3 (optional): which buttons should be added to the MessageBox (Ok, YesNo, YesNoCancel, ...)
65 | //Parameter 4 (optional): which icon should be displayed and which sound shoulc be played when the MessageBox is shown
66 | //Parameter 5 (optinoal): which button should be the predefined default button
67 | MessageBox.Show("This space is already occupied, please choose another one.", "Can't occupy", MessageBoxButton.OK, MessageBoxImage.Exclamation);
68 | }
69 |
70 | ///
71 | /// Handles the event and displays that no player has won. Afterwards the players are asked, if
72 | /// they want to play again
73 | ///
74 | private void OnGameOverDraw(object sender, EventArgs e)
75 | {
76 | MessageBoxResult result = MessageBox.Show("The game ended in a draw. Do you want to play again?", "GameOver", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
77 | this.ProcessGameOverPlayerChoice(result);
78 | }
79 |
80 | ///
81 | /// Handles the event and displays that the player specified in
82 | /// has won. Afterwards the players are asked, if they want to play again
83 | ///
84 | private void OnPlayerWon(object sender, PlayerEventArgs e)
85 | {
86 | MessageBoxResult result = MessageBox.Show($"Player {e.Player.Id} won the game. Do you want to play again?", "GameOver", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
87 | this.ProcessGameOverPlayerChoice(result);
88 | }
89 |
90 | ///
91 | /// Processes the play-again-prompt-result. If is the game is restarted,
92 | /// otherwise the window and therefore the application is closed
93 | ///
94 | private void ProcessGameOverPlayerChoice(MessageBoxResult result)
95 | {
96 | if (result == MessageBoxResult.Yes)
97 | {
98 | this.gameState.StartGame();
99 | }
100 | else
101 | {
102 | //Closes the current window.
103 | //If there is no open Window and (Application.Current.ShutdownMode == ShutdownMode.OnLastWindowClose)
104 | // or the main window is closed (Application.Current.ShutdownMode == ShutdownMode.OnMainWindowClose)
105 | //the application will be terminated as well
106 | //If (Application.Current.ShutdownMode == ShutdownMode.OnExplicitShutdown) you have to call Application.Current.Shutdown() yourself
107 | this.Close();
108 | }
109 | }
110 |
111 | ///
112 | /// Handled the event and resets the buttons to their blank starting state
113 | ///
114 | private void OnTilesReset(object sender, EventArgs e)
115 | {
116 | foreach (ButtonTileMapping mapping in this.buttonTileMappings)
117 | {
118 | mapping.Button.Content = "";
119 | }
120 | }
121 |
122 | ///
123 | /// Handles the click event of the buttons of the window
124 | ///
125 | private void OnButtonClicked(object sender, RoutedEventArgs e)
126 | {
127 | //Here we use a "Type pattern" (https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/is#type-pattern)
128 | //(sender is Button) checks if the variable "sender" is of type Button
129 | //if this is the case, sender will be assigned to the variable "Button" which can be used in the scope of the if-statement
130 | if (sender is Button button)
131 | {
132 | //get the ButtonTileMapping for the clicked Button
133 | ButtonTileMapping mapping = this.buttonTileMappings.Single(_mapping => _mapping.Button == button);
134 | //Tells the GameState to occupy the tile of the mapping
135 | this.gameState.OccupyTile(mapping);
136 | }
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/Projects/LunarDoggo.QuizGame/GameState.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System;
4 |
5 | namespace LunarDoggo.QuizGame
6 | {
7 | ///
8 | /// This class is a data container that contains all data regarding the game state
9 | ///
10 | public class GameState
11 | {
12 | //all answered questions are stored with the flag if the given answer was correct or wrong
13 | private readonly Dictionary givenAnswers = new Dictionary();
14 | private readonly List unansweredQuestions = new List();
15 | //Random for choosing a random unanswered question from the list
16 | //Note: The default constructor of Random in most languages takes the current system time as a seed for this reason,
17 | //Random-instances that are instantiated in loops can sometimes have the same seed and therefore return the same values
18 | private readonly Random random = new Random();
19 |
20 | //index of the currently highlighted answer in the answer array of the current question
21 | private int highlightedAnswerIndex;
22 |
23 | ///
24 | /// Instantiates a new -object with the provided s
25 | ///
26 | public GameState(IEnumerable questions)
27 | {
28 | if (questions != null && questions.Any())
29 | {
30 | this.unansweredQuestions.AddRange(questions);
31 | this.TotalQuestionCount = this.unansweredQuestions.Count;
32 | }
33 | }
34 |
35 | ///
36 | /// Returns the currently hightlighted
37 | ///
38 | public QuizQuestionAnswer HighlightedAnswer { get { return this.CurrentQuestion?.Answers[this.highlightedAnswerIndex]; } }
39 |
40 | ///
41 | /// Returns the count of answers the user got right
42 | ///
43 | public int CorrectAnswersCount
44 | {
45 | //With the extension methods provided by System.Linq.Enumerable you can write Lambda-Expressions which are like anonymous/nameless methods that operate on a
46 | //given dataset (array, list, database, xml, ...). In this case we use the method "Where" which takes an expression that iterates the QuizQuestion-instances of givenAnswers, does some
47 | //Format: ...Where({temporary variable name} => {expression that returns a boolean})
48 | //learn more about lambda-expressions: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions
49 | //learn more about System.Linq.Enumerable extension methods:
50 | // - available methods: https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable?view=net-5.0
51 | // - how to use them: https://www.dotnetperls.com/ienumerable
52 | get { return this.givenAnswers.Where(_pair => _pair.Value).Count(); }
53 | }
54 |
55 | ///
56 | /// Returns whether there are unanswered s left or not
57 | ///
58 | public bool HasUnansweredQuestions { get { return this.unansweredQuestions.Count > 0; } }
59 |
60 | ///
61 | /// Returns the count of s the user has already answered
62 | ///
63 | public int AnsweredQuestionCount { get { return this.givenAnswers.Count; } }
64 |
65 | ///
66 | /// Returns the the user has chosen for
67 | ///
68 | public QuizQuestionAnswer ChosenAnswer { get; private set; }
69 |
70 | ///
71 | /// Returns whether the user already answered
72 | ///
73 | public bool IsCurrentQuestionAnswered { get; private set; }
74 |
75 | ///
76 | /// Returns the currently active
77 | ///
78 | public QuizQuestion CurrentQuestion { get; private set; }
79 |
80 | ///
81 | /// Returns the total count of s in the game
82 | ///
83 | public int TotalQuestionCount { get; }
84 |
85 | ///
86 | /// Chooses the next to be asked
87 | ///
88 | public void MoveToNextQuestion()
89 | {
90 | if (this.HasUnansweredQuestions) //if there are unanswered questions left, we want to choose the next one
91 | {
92 | int questionIndex = random.Next(0, this.unansweredQuestions.Count); //randomly choose the index of the next question from GameState.unansweredQuestions
93 | this.CurrentQuestion = this.unansweredQuestions[questionIndex];
94 | //Reset the data regarding the last question
95 | this.IsCurrentQuestionAnswered = false;
96 | this.highlightedAnswerIndex = 0;
97 | this.ChosenAnswer = null;
98 | }
99 | }
100 |
101 | ///
102 | /// This method sets the next answer to be highlighted, it wraps around if the last answer is passed
103 | ///
104 | public void HighlightNextAnswer()
105 | {
106 | this.ChangeHighlightedAnswer(1);
107 | }
108 |
109 | ///
110 | /// This method sets the previous answer to be highlighted, it wraps around if the first answer is passed
111 | ///
112 | public void HighlightPreviousAnswer()
113 | {
114 | this.ChangeHighlightedAnswer(-1);
115 | }
116 |
117 | ///
118 | /// This method sets the answer to be highlighted depending on the provided increment, it wraps around if the last or first
119 | /// answer if is passed
120 | ///
121 | private void ChangeHighlightedAnswer(int indexIncrement)
122 | {
123 | int answerCount = this.CurrentQuestion.Answers.Length;
124 | this.highlightedAnswerIndex += indexIncrement;
125 |
126 | if (this.highlightedAnswerIndex >= answerCount)
127 | {
128 | this.highlightedAnswerIndex = 0;
129 | }
130 | else if (this.highlightedAnswerIndex < 0)
131 | {
132 | this.highlightedAnswerIndex = answerCount - 1;
133 | }
134 | }
135 |
136 | ///
137 | /// Sets answered and updates the related data
138 | ///
139 | public void AnswerQuestion()
140 | {
141 | if (this.CurrentQuestion != null)
142 | {
143 | QuizQuestionAnswer givenAnswer = this.CurrentQuestion.Answers[highlightedAnswerIndex];
144 | //remove the current question from the list of unanswered questions so it isn't asked again
145 | this.unansweredQuestions.Remove(this.CurrentQuestion);
146 | //add the current question and whether the answer was correct to GameState.givenAnswers for the final result
147 | this.givenAnswers.Add(this.CurrentQuestion, givenAnswer.IsCorrect);
148 | this.IsCurrentQuestionAnswered = true;
149 | this.ChosenAnswer = givenAnswer;
150 | }
151 | }
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.toptal.com/developers/gitignore/api/visualstudio
3 | # Edit at https://www.toptal.com/developers/gitignore?templates=visualstudio
4 |
5 | ### VisualStudio ###
6 | ## Ignore Visual Studio temporary files, build results, and
7 | ## files generated by popular Visual Studio add-ons.
8 | ##
9 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
10 |
11 | # User-specific files
12 | *.rsuser
13 | *.suo
14 | *.user
15 | *.userosscache
16 | *.sln.docstates
17 |
18 | # User-specific files (MonoDevelop/Xamarin Studio)
19 | *.userprefs
20 |
21 | # Mono auto generated files
22 | mono_crash.*
23 |
24 | # Build results
25 | [Dd]ebug/
26 | [Dd]ebugPublic/
27 | [Rr]elease/
28 | [Rr]eleases/
29 | x64/
30 | x86/
31 | [Aa][Rr][Mm]/
32 | [Aa][Rr][Mm]64/
33 | bld/
34 | [Bb]in/
35 | [Oo]bj/
36 | [Ll]og/
37 | [Ll]ogs/
38 |
39 | # Visual Studio 2015/2017 cache/options directory
40 | .vs/
41 | # Uncomment if you have tasks that create the project's static files in wwwroot
42 | #wwwroot/
43 |
44 | # Visual Studio 2017 auto generated files
45 | Generated\ Files/
46 |
47 | # MSTest test Results
48 | [Tt]est[Rr]esult*/
49 | [Bb]uild[Ll]og.*
50 |
51 | # NUnit
52 | *.VisualState.xml
53 | TestResult.xml
54 | nunit-*.xml
55 |
56 | # Build Results of an ATL Project
57 | [Dd]ebugPS/
58 | [Rr]eleasePS/
59 | dlldata.c
60 |
61 | # Benchmark Results
62 | BenchmarkDotNet.Artifacts/
63 |
64 | # .NET Core
65 | project.lock.json
66 | project.fragment.lock.json
67 | artifacts/
68 |
69 | # StyleCop
70 | StyleCopReport.xml
71 |
72 | # Files built by Visual Studio
73 | *_i.c
74 | *_p.c
75 | *_h.h
76 | *.ilk
77 | *.meta
78 | *.obj
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*[.json, .xml, .info]
147 |
148 | # Visual Studio code coverage results
149 | *.coverage
150 | *.coveragexml
151 |
152 | # NCrunch
153 | _NCrunch_*
154 | .*crunch*.local.xml
155 | nCrunchTemp_*
156 |
157 | # MightyMoose
158 | *.mm.*
159 | AutoTest.Net/
160 |
161 | # Web workbench (sass)
162 | .sass-cache/
163 |
164 | # Installshield output folder
165 | [Ee]xpress/
166 |
167 | # DocProject is a documentation generator add-in
168 | DocProject/buildhelp/
169 | DocProject/Help/*.HxT
170 | DocProject/Help/*.HxC
171 | DocProject/Help/*.hhc
172 | DocProject/Help/*.hhk
173 | DocProject/Help/*.hhp
174 | DocProject/Help/Html2
175 | DocProject/Help/html
176 |
177 | # Click-Once directory
178 | publish/
179 |
180 | # Publish Web Output
181 | *.[Pp]ublish.xml
182 | *.azurePubxml
183 | # Note: Comment the next line if you want to checkin your web deploy settings,
184 | # but database connection strings (with potential passwords) will be unencrypted
185 | *.pubxml
186 | *.publishproj
187 |
188 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
189 | # checkin your Azure Web App publish settings, but sensitive information contained
190 | # in these scripts will be unencrypted
191 | PublishScripts/
192 |
193 | # NuGet Packages
194 | *.nupkg
195 | # NuGet Symbol Packages
196 | *.snupkg
197 | # The packages folder can be ignored because of Package Restore
198 | **/[Pp]ackages/*
199 | # except build/, which is used as an MSBuild target.
200 | !**/[Pp]ackages/build/
201 | # Uncomment if necessary however generally it will be regenerated when needed
202 | #!**/[Pp]ackages/repositories.config
203 | # NuGet v3's project.json files produces more ignorable files
204 | *.nuget.props
205 | *.nuget.targets
206 |
207 | # Microsoft Azure Build Output
208 | csx/
209 | *.build.csdef
210 |
211 | # Microsoft Azure Emulator
212 | ecf/
213 | rcf/
214 |
215 | # Windows Store app package directories and files
216 | AppPackages/
217 | BundleArtifacts/
218 | Package.StoreAssociation.xml
219 | _pkginfo.txt
220 | *.appx
221 | *.appxbundle
222 | *.appxupload
223 |
224 | # Visual Studio cache files
225 | # files ending in .cache can be ignored
226 | *.[Cc]ache
227 | # but keep track of directories ending in .cache
228 | !?*.[Cc]ache/
229 |
230 | # Others
231 | ClientBin/
232 | ~$*
233 | *~
234 | *.dbmdl
235 | *.dbproj.schemaview
236 | *.jfm
237 | *.pfx
238 | *.publishsettings
239 | orleans.codegen.cs
240 |
241 | # Including strong name files can present a security risk
242 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
243 | #*.snk
244 |
245 | # Since there are multiple workflows, uncomment next line to ignore bower_components
246 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
247 | #bower_components/
248 |
249 | # RIA/Silverlight projects
250 | Generated_Code/
251 |
252 | # Backup & report files from converting an old project file
253 | # to a newer Visual Studio version. Backup files are not needed,
254 | # because we have git ;-)
255 | _UpgradeReport_Files/
256 | Backup*/
257 | UpgradeLog*.XML
258 | UpgradeLog*.htm
259 | ServiceFabricBackup/
260 | *.rptproj.bak
261 |
262 | # SQL Server files
263 | *.mdf
264 | *.ldf
265 | *.ndf
266 |
267 | # Business Intelligence projects
268 | *.rdl.data
269 | *.bim.layout
270 | *.bim_*.settings
271 | *.rptproj.rsuser
272 | *- [Bb]ackup.rdl
273 | *- [Bb]ackup ([0-9]).rdl
274 | *- [Bb]ackup ([0-9][0-9]).rdl
275 |
276 | # Microsoft Fakes
277 | FakesAssemblies/
278 |
279 | # GhostDoc plugin setting file
280 | *.GhostDoc.xml
281 |
282 | # Node.js Tools for Visual Studio
283 | .ntvs_analysis.dat
284 | node_modules/
285 |
286 | # Visual Studio 6 build log
287 | *.plg
288 |
289 | # Visual Studio 6 workspace options file
290 | *.opt
291 |
292 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
293 | *.vbw
294 |
295 | # Visual Studio LightSwitch build output
296 | **/*.HTMLClient/GeneratedArtifacts
297 | **/*.DesktopClient/GeneratedArtifacts
298 | **/*.DesktopClient/ModelManifest.xml
299 | **/*.Server/GeneratedArtifacts
300 | **/*.Server/ModelManifest.xml
301 | _Pvt_Extensions
302 |
303 | # Paket dependency manager
304 | .paket/paket.exe
305 | paket-files/
306 |
307 | # FAKE - F# Make
308 | .fake/
309 |
310 | # CodeRush personal settings
311 | .cr/personal
312 |
313 | # Python Tools for Visual Studio (PTVS)
314 | __pycache__/
315 | *.pyc
316 |
317 | # Cake - Uncomment if you are using it
318 | # tools/**
319 | # !tools/packages.config
320 |
321 | # Tabs Studio
322 | *.tss
323 |
324 | # Telerik's JustMock configuration file
325 | *.jmconfig
326 |
327 | # BizTalk build output
328 | *.btp.cs
329 | *.btm.cs
330 | *.odx.cs
331 | *.xsd.cs
332 |
333 | # OpenCover UI analysis results
334 | OpenCover/
335 |
336 | # Azure Stream Analytics local run output
337 | ASALocalRun/
338 |
339 | # MSBuild Binary and Structured Log
340 | *.binlog
341 |
342 | # NVidia Nsight GPU debugger configuration file
343 | *.nvuser
344 |
345 | # MFractors (Xamarin productivity tool) working folder
346 | .mfractor/
347 |
348 | # Local History for Visual Studio
349 | .localhistory/
350 |
351 | # BeatPulse healthcheck temp database
352 | healthchecksdb
353 |
354 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
355 | MigrationBackup/
356 |
357 | # Ionide (cross platform F# VS Code tools) working folder
358 | .ionide/
359 |
360 | # End of https://www.toptal.com/developers/gitignore/api/visualstudio
361 |
--------------------------------------------------------------------------------
/Datastructures/Collections/ArrayList.cs:
--------------------------------------------------------------------------------
1 | namespace LunarDoggo.Datastructures.Collections
2 | {
3 | //An array list ist a list that uses an array for data storage with one twist: array lists are usually implemented to have
4 | //variable length in contrast to arrays which have a fixed length. To achieve that goal, array lists keep track of the number
5 | //of items stored and allocate a new array with a bigger or smaller size, as soon as a item count threshold is met. Most
6 | //commonly array lists double in size if the array is half filled. Most basic implementations don't shrink the array if most
7 | //items are removed from it, but this implementation showcases how shrinking the underlying array could work, allocating a new
8 | //smaller array with half the size of the original one if a item count threshold of 1/8th the length of the array is met.
9 | //To ensure that the array always stays viable, the minimum array size is set to 8
10 | //In contrast to linked lists, the time complexity of deleting an item is O(n) instead of O(1) with n being the number of items
11 | //that is currently stored, but array lists often are preferred over linked lists, as they are generally easier to work with,
12 | //because you don't have to keep track of LinkedListItems and therefore you cannot break the list by accident that easily
13 | public class ArrayList where T : class
14 | {
15 | ///
16 | /// Keeps track of the index of the last item in the list
17 | ///
18 | private int currentIndex = -1;
19 | ///
20 | /// Stores all items contained in the list
21 | ///
22 | private T[] items;
23 |
24 | //Array lists don't require a initial capacity, as they are variable in size. In this implementation a initial capacity
25 | //8 items is chosen by default
26 | public ArrayList() : this(8)
27 | { }
28 |
29 | //But optionally, a initial capacity can be provided, if it is known that right after initialization, a lot of new items will
30 | //be aded to the storage array
31 | public ArrayList(int initialCapacity)
32 | {
33 | this.items = new T[Math.Max(initialCapacity, 8)];
34 | }
35 |
36 | ///
37 | /// Adds a new item to the array list, resizing the storage array as needed. Afterwards the index of the added item is returned
38 | ///
39 | public int Add(T item)
40 | {
41 | //The add operation has a time complexity of O(n), as sometimes the array has to be copied. But for this example the amortized
42 | //time complexity (i.e. the average time complexity over many operations) is O(1), as the array is rarely copied for large n's
43 | this.currentIndex++;
44 | this.items[this.currentIndex] = item;
45 |
46 | //if the array is halfway full, it will be resized to twice its length to ensure that there's enough room for more items
47 | //to be added
48 | if (this.currentIndex > this.items.Length / 2)
49 | {
50 | this.CopyToNewArray(this.items.Length * 2);
51 | }
52 |
53 | return this.currentIndex;
54 | }
55 |
56 | ///
57 | /// Removes the item at the provided index if it exists
58 | ///
59 | public void RemoveAt(int index)
60 | {
61 | //The remove operation has a time complexity of O(n), as, in the worst case, every item in the array has
62 | //to be shifted one cell to the left. The amortized time complexity therefore also is O(n), even though
63 | //copying the array also rarely occurs for large n's
64 | //As just setting the default value to the removed item would leave gaps and replacing the removed item
65 | //with the last item in the list would shuffle the list over time, every item after the removed index
66 | //has to be shifted one cell to the left. The last index of the array has to be set to the default value
67 | //of type T
68 | for (int i = index; index < this.items.Length - 1; index++)
69 | {
70 | this.items[i] = this.items[i + 1];
71 | }
72 | this.items[this.items.Length - 1] = default;
73 | this.currentIndex--;
74 |
75 | //In this implementation the array will be shrunk if the array is less than 1/8th filled after removing an item
76 | if (this.items.Length > 0.125 * this.currentIndex)
77 | {
78 | this.CopyToNewArray(this.items.Length / 2);
79 | }
80 | }
81 |
82 | ///
83 | /// Returns the index of the first occurence of the provided item is stored at
84 | ///
85 | public int IndexOf(T item)
86 | {
87 | //The IndexOf operation does the same job, as the Search operation of a LinkedList, but instead of returning
88 | //a LinkedListITem, it returns the index an item is stored at. Therefore both have the same time complexity
89 | //of O(n)
90 |
91 | //A return value of -1 signifies that the item isn't present in the list
92 | if (this.IsEmpty)
93 | {
94 | return -1;
95 | }
96 |
97 | //this particular search algorithm terminates after the first ocurrence of the searched item is found
98 | int itemIndex = -1;
99 | int index = 0;
100 | while (index <= this.currentIndex && itemIndex == -1)
101 | {
102 | if (item == this.items[index])
103 | {
104 | itemIndex = index;
105 | }
106 | itemIndex++;
107 | }
108 |
109 | return itemIndex;
110 | }
111 |
112 | ///
113 | /// Returns the item that is stored at the provided index
114 | ///
115 | public T Get(int index)
116 | {
117 | //Note: in C# you would idiomatically use the indexer, but this implementation uses a get method
118 | // to keep it more generalized
119 |
120 | //The get operation has a time complexity of O(1), as the memory address of the storage cell can
121 | //be directly calculated and accessed
122 | return this.items[index];
123 | }
124 |
125 | ///
126 | /// Returns whether this array list is empty
127 | ///
128 | public bool IsEmpty
129 | {
130 | //if currentIndex is -1, no cell of the array has been written to yet
131 | get => this.currentIndex == -1;
132 | }
133 |
134 | ///
135 | /// Returns the amount of items currently stored in the array list
136 | ///
137 | public int Count
138 | {
139 | //As arrays use 0-based indexing in C#, you must add one to the current storage index to convert it to an item count
140 | get => this.currentIndex + 1;
141 | }
142 |
143 | ///
144 | /// This method is responsible for copying the current storage array to a new one with a different size
145 | ///
146 | private void CopyToNewArray(int newLength)
147 | {
148 | //This operation has a time complexity of O(n), but it is executed so rarely for large n's, that it
149 | //doesn't affect the amortized time complexity of the add and remove operations
150 |
151 | //To prevent unnecessary copy operations, the minimum array length is set to 8 and copying only
152 | //occurs if the new array length is different from the current one
153 | newLength = Math.Max(newLength, 8);
154 | if (newLength != this.items.Length)
155 | {
156 | //Note: C# also provides the method Array.Resize(ref T[] array, int newSize) that achieves the same goal
157 | // but a) it sometimes has to copy the array content to a new array if there isn't enough continous
158 | // free memory after the array
159 | // and b) it's a C# specific method that may not be available in other languages (keep it generalized)
160 | T[] copy = new T[newLength];
161 | for (int i = 0; i < Math.Min(newLength, this.items.Length); i++)
162 | {
163 | copy[i] = this.items[i];
164 | }
165 | this.items = copy;
166 | }
167 | }
168 | }
169 | }
--------------------------------------------------------------------------------