diff --git a/031_Challenge_mini_game/031_csharp.md b/031_Challenge_mini_game/031_csharp.md new file mode 100644 index 0000000..2c1b775 --- /dev/null +++ b/031_Challenge_mini_game/031_csharp.md @@ -0,0 +1,297 @@ +# Challenge project + +## Create a mini-game + +Demonstrate your ability to create and use different methods to develop +features for a console mini-game. + +### Learning objectives + +- Develop a C# console application that uses methods to implement logical +workflows. + +- Understand existing code and make informed changes to design. + +- Create return values as well as required and optional parameters in methods. + +## Introduction + +Creating your own game is an exciting way to practice your programming skills. +Games rely heavily on processing user input to make dynamic decisions. Every +game must also have a set of defined rules that determine actions and events in +the game. + +Suppose you want to create your own game. You might not be ready to develop a +fully featured game, so you decide to start as small as possible. You want to +move a character across the screen and make it consume an object. The object +consume can affect the state of the player. To keep the game going, you wanted +to regenerate the object in a new location once it has been consumed. You +decide that you'll need to use methods to keep your game code organized. + +In this module, you'll develop the following features of a mini-game +application: + +- A feature to determine if the player consumed the food +- A feature that updates player status depending on the food consumed +- A feature that pauses movement speed depending on the food consumed +- A feature to regenerate food in a new location +- An option to terminate the game if an unsupported character is pressed +- A feature to terminate the game if the Terminal window was resized + +By the end of this module, you'll create a playable mini-game application! + +> Note +> This is a challenge project module where you'll complete an end-to-end +project from a specification. This module is intended to be a test of your +skills; there’s little guidance and no step-by-step instructions. + +#### Learning objectives + +In this module, you'll demonstrate your ability to: + +- Use Visual Studio Code to develop a C# console application that uses methods +to implement logical workflows. +- Understand existing code and make informed changes to design. +- Create return values and methods with required and optional parameters. + +--- + +## Prepare for challenge + +You'll be using Visual Studio Code to develop a small mini-game. Your +application should establish the basics of the game, including updating player +state, manipulating player movement, and consuming and regenerating a food +object. You'll develop each of those features and run a simplified game test. + +### Project specification + +The Starter code project for this module includes a Program.cs file with the +following code features: + +```txt +- The code declares the following variables: + - Variables to determine the size of the Terminal window. + - Variables to track the locations of the player and food. + - Arrays `states` and `foods` to provide available player and food appearances + - Variables to track the current player and food appearance + +- The code provides the following methods: + - A method to determine if the Terminal window was resized. + - A method to display a random food appearance at a random location. + - A method that changes the player appearance to match the food consumed. + - A method that temporarily freezes the player movement. + - A method that moves the player according to directional input. + - A method that sets up the initial game state. + +- The code doesn't call the methods correctly to make the game playable. The following features are missing: + - Code to determine if the player has consumed the food displayed. + - Code to determine if the food consumed should freeze player movement. + - Code to determine if the food consumed should increase player movement. + - Code to increase movement speed. + - Code to redisplay the food after it's consumed by the player. + - Code to terminate execution if an unsupported key is entered. + - Code to terminate execution if the terminal was resized. +``` + +Your goal in this challenge is to use the existing features and create the +missing features to make the game playable. + +### Setup + +Use the following steps to prepare for the Challenge project exercises: + +To download a zip file containing the Starter project code, select the +following link: +[Lab Files](https://github.com/MicrosoftLearning/Challenge-project-Create-methods-in-CSharp/archive/refs/heads/main.zip). + +Unzip the download files. + +You're now ready to begin the Challenge project exercises. Good luck! + +--- + +## Exercise - Add code to end the game + +Your goal is to develop a mini-game application. You need the game to end if +the user resized the Console window the game is running in. You also want to +add an option for the game to end if the user enters any nondirectional +character. + +### Specification + +In this challenge exercise, you need to update the existing code to support an +option to terminate the gameplay if a nondirectional character is entered. You +also want to terminate the game if the terminal window was resized. You need to +locate the correct methods for your code to use. + +#### Terminate on resize + +This feature must: + +- Determine if the terminal was resized before allowing the game to continue +- Clear the Console and end the game if the terminal was resized +- Display the following message before ending the program: Console was resized. Program exiting. + +#### Add optional termination + +- Modify the existing `Move` method to support an optional parameter +- If enabled, the optional parameter should detect nondirectional key input +- If nondirectional input is detected, allow the game to terminate + +### Check your work + +To validate that your code satisfies the specified requirements, complete the +following steps: + +At the Terminal command prompt, resize the window. + +Enter a directional key. + +Verify that the program ends after displaying the following message: + +```txt +Console was resized. Program exiting. +``` + +Run the app again. + +At the Terminal command prompt, press directional keys to move the player. + +Press a nondirectional key. + +Verify that the program ends. + +Disable the optional parameter, then build and run the app. + +At the Terminal command prompt, press directional keys to move the player. + +Press a nondirectional key. + +Verify that the program continues. + +Resize the Terminal window. + +Verify that the program ends. + +Once you've validated the results for this exercise, proceed to the next +exercise in this challenge. + +--- + +## Exercise + +### Make the player consume food + +Your goal is to develop a mini-game application. The mini-game displays food +that the player can consume. You need to detect if the player has successfully +consumed the food, and if so, redisplay the food. You also want to change the +player appearance depending on what food was consumed. + +### Specification + +In this challenge exercise, you need to create a method that determines if the +player has consumed the food that was displayed. If the food was consumed, you +want to update the player's appearance and redisplay the food. + +#### Check if the player consumed the food + +- Create a method that uses the existing position variables of the player and food +- The method should return a value +- After the user moves the character, call your method to determine the following: + - Whether or not to use the existing method that changes player appearance + - Whether or not to use the existing method to redisplay the food + +### Check your work + +To validate that your code satisfies the specified requirements, complete the +following steps: + +At the Terminal command prompt, press directional keys to move the player. + +Move the player across the displayed food string. + +Verify that a new food string is displayed. + +Verify that the player appearance changes depending on which food string was +consumed. + +Once you've validated the results for this exercise, proceed to the next +exercise in this challenge. + +--- + +## Exercise + +### Add code to modify movement + +Your goal is to develop a mini-game application. Currently, your mini-game has +some basic gameplay capabilities! It terminates correctly, detects when the +player consumes food, changes the player appearance, and displays more food. +Now you want the food the player consumes to affect the player's ability to +move. + +### Specification + +In this challenge exercise, you need to create a method that determines if the +player has consumed the food that affects their movement. When the player +consumes the food string with value `#####,` the appearance is updated to +`(X_X)`. You'll add a feature to detect if the player appearance is `(X_X)`, and +if so, temporarily prevent the player from moving. + +You also want to add an optional feature that detects if the player appearance +is `(^-^)` and if so, increase or decrease the right and left movement speeds +by a value of `3` while that appearance is active. When the player state is +`('-')`, you want the speed to return to normal. You want to make this feature +optional since consuming food in this state requires more collision detection +than you want to develop for now. + +#### Check if the player should freeze + +- Create a method that checks if the current player appearance is `(X_X)` +- The method should return a value +- Before allowing the user to move the character, call your method to determine the following: + - Whether or not to use the existing method that freezes character movement +- Make sure the character is only frozen temporarily and the player can still move afterwards + +#### Add an option to increase player speed + +- Modify the existing `Move` method to support an optional movement speed +parameter +- Use the parameter to increase or decrease right and left movement speed by `3` +- Create a method that checks if the current player appearance is `(^-^)` +- The method should return a value +- Call your method to determine if `Move` should use the movement speed +parameter + +### Check your work + +To validate that your code satisfies the specified requirements, complete the +following steps: + +Enable the optional parameters. + +At the Terminal command prompt, press directional keys to move the player. + +Move the player across the displayed food string. + +Verify that a new food string is displayed. + +Verify that the player appearance changes depending on which food string was +consumed. + +Verify that movement is temporarily stopped when the player appearance is +`(X_X)`. + +Verify that left and right movement is faster in the correct directions when +the player appearance is `(^-^)`. + +Press a nondirectional key to terminate the program. + +Disable the optional movement speed parameter and rerun the app. + +Verify that movement is normal when the player appearance is `(^-^)`. + +Congratulations if you succeeded in this challenge! + +--- + diff --git a/031_Challenge_mini_game/Project_minigame/Final/Final.csproj b/031_Challenge_mini_game/Project_minigame/Final/Final.csproj new file mode 100644 index 0000000..74abf5c --- /dev/null +++ b/031_Challenge_mini_game/Project_minigame/Final/Final.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/031_Challenge_mini_game/Project_minigame/Final/Program.cs b/031_Challenge_mini_game/Project_minigame/Final/Program.cs new file mode 100644 index 0000000..3f55553 --- /dev/null +++ b/031_Challenge_mini_game/Project_minigame/Final/Program.cs @@ -0,0 +1,144 @@ +using System; + +Random random = new Random(); +Console.CursorVisible = false; +int height = Console.WindowHeight - 1; +int width = Console.WindowWidth - 5; +bool should_exit = false; + +// Console position of the player +int player_x = 0; +int player_y = 0; + +// Console position of the food +int food_x = 0; +int food_y = 0; + +// Available player and food strings +string[] states = { "('-')", "(^-^)", "(X_X)" }; +string[] foods = { "@@@@@", "$$$$$", "#####" }; + +// Current player string displayed in the Console +string player = states[0]; + +// Index of the current food +int food = 0; + +initialize_game(); +while (!should_exit) { + if (terminal_resized()) { + Console.Clear(); + Console.Write("Console was resized. Program exiting."); + should_exit = true; + } else { + if (player_is_faster()) { + move(1, false); + } else if (player_is_sick()) { + freeze_player(); + } else { + move(otherKeysExit: false); + } + if (got_food()) { + change_player(); + show_food(); + } + } +} + +// Returns true if the Terminal was resized +bool terminal_resized() { + return height != Console.WindowHeight - 1 || + width != Console.WindowWidth - 5; +} + +// Displays random food at a random location +void show_food() { + // Update food to a random index + food = random.Next(0, foods.Length); + + // Update food position to a random location + food_x = random.Next(0, width - player.Length); + food_y = random.Next(0, height - 1); + + // Display the food at the location + Console.SetCursorPosition(food_x, food_y); + Console.Write(foods[food]); +} + +// Returns true if the player location matches the food location +bool got_food() { + return player_y == food_y && player_x == food_x; +} + +// Returns true if the player appearance represents a sick state +bool player_is_sick() { + return player.Equals(states[2]); +} + +// Returns true if the player appearance represents a fast state +bool player_is_faster() { + return player.Equals(states[1]); +} + +// Changes the player to match the food consumed +void change_player() { + player = states[food]; + Console.SetCursorPosition(player_x, player_y); + Console.Write(player); +} + +// Temporarily stops the player from moving +void freeze_player() { + System.Threading.Thread.Sleep(1000); + player = states[0]; +} + +// Reads directional input from the Console and moves the player +void move(int speed = 1, bool otherKeysExit = false) { + int last_x = player_x; + int last_y = player_y; + + switch (Console.ReadKey(true).Key) { + case ConsoleKey.UpArrow: + player_y--; + break; + case ConsoleKey.DownArrow: + player_y++; + break; + case ConsoleKey.LeftArrow: + player_x -= speed; + break; + case ConsoleKey.RightArrow: + player_x += speed; + break; + case ConsoleKey.Escape: + should_exit = true; + break; + default: + // Exit if any other keys are pressed + should_exit = otherKeysExit; + break; + } + + // Clear the characters at the previous position + Console.SetCursorPosition(last_x, last_y); + for (int i = 0; i < player.Length; i++) { + Console.Write(" "); + } + + // Keep player position within the bounds of the Terminal window + player_x = (player_x < 0) ? 0 : (player_x >= width ? width : player_x); + player_y = (player_y < 0) ? 0 : (player_y >= height ? height : player_y); + + // Draw the player at the new location + Console.SetCursorPosition(player_x, player_y); + Console.Write(player); +} + +// Clears the console, displays the food and player +void initialize_game() { + Console.Clear(); + show_food(); + Console.SetCursorPosition(0, 0); + Console.Write(player); +} diff --git a/031_Challenge_mini_game/Project_minigame/LICENSE b/031_Challenge_mini_game/Project_minigame/LICENSE new file mode 100644 index 0000000..2080d95 --- /dev/null +++ b/031_Challenge_mini_game/Project_minigame/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Microsoft Learning + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/031_Challenge_mini_game/Project_minigame/Starter/Program.cs b/031_Challenge_mini_game/Project_minigame/Starter/Program.cs new file mode 100644 index 0000000..b7dca71 --- /dev/null +++ b/031_Challenge_mini_game/Project_minigame/Starter/Program.cs @@ -0,0 +1,109 @@ +using System; + +Random random = new Random(); +Console.CursorVisible = false; +int height = Console.WindowHeight - 1; +int width = Console.WindowWidth - 5; +bool should_exit = false; + +// Console position of the player +int player_x = 0; +int player_y = 0; + +// Console position of the food +int food_x = 0; +int food_y = 0; + +// Available player and food strings +string[] states = { "('-')", "(^-^)", "(X_X)" }; +string[] foods = { "@@@@@", "$$$$$", "#####" }; + +// Current player string displayed in the Console +string player = states[0]; + +// Index of the current food +int food = 0; + +initialize_game(); +while (!should_exit) { + move(); +} + +// Returns true if the Terminal was resized +bool terminal_resized() { + return height != Console.WindowHeight - 1 || + width != Console.WindowWidth - 5; +} + +// Displays random food at a random location +void show_food() { + // Update food to a random index + food = random.Next(0, foods.Length); + + // Update food position to a random location + food_x = random.Next(0, width - player.Length); + food_y = random.Next(0, height - 1); + + // Display the food at the location + Console.SetCursorPosition(food_x, food_y); + Console.Write(foods[food]); +} + +// Changes the player to match the food consumed +void change_player() { + player = states[food]; + Console.SetCursorPosition(player_x, player_y); + Console.Write(player); +} + +// Temporarily stops the player from moving +void freeze_player() { + System.Threading.Thread.Sleep(1000); + player = states[0]; +} + +// Reads directional input from the Console and moves the player +void move() { + int last_x = player_x; + int last_y = player_y; + + switch (Console.ReadKey(true).Key) { + case ConsoleKey.UpArrow: + player_y--; + break; + case ConsoleKey.DownArrow: + player_y++; + break; + case ConsoleKey.LeftArrow: + player_x--; + break; + case ConsoleKey.RightArrow: + player_x++; + break; + case ConsoleKey.Escape: + should_exit = true; + break; + } + + // Clear the characters at the previous position + Console.SetCursorPosition(last_x, last_y); + for (int i = 0; i < player.Length; i++) { + Console.Write(" "); + } + + // Keep player position within the bounds of the Terminal window + player_x = (player_x < 0) ? 0 : (player_x >= width ? width : player_x); + player_y = (player_y < 0) ? 0 : (player_y >= height ? height : player_y); + + // Draw the player at the new location + Console.SetCursorPosition(player_x, player_y); + Console.Write(player); +} + +// Clears the console, displays the food and player +void initialize_game() { + Console.Clear(); + show_food(); + Console.SetCursorPosition(0, 0); + Console.Write(player); +} diff --git a/031_Challenge_mini_game/Project_minigame/Starter/Starter.csproj b/031_Challenge_mini_game/Project_minigame/Starter/Starter.csproj new file mode 100644 index 0000000..74abf5c --- /dev/null +++ b/031_Challenge_mini_game/Project_minigame/Starter/Starter.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/README.md b/README.md index 171d259..34da81d 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,4 @@ Following 28. [Methods with parameters](./028_method_parameters/028_csharp.md) 29. [Methods that return values](./029_return_value_method/029_csharp.md) 30. [Guided project - Plan a Petting Zoo](./030_project_petting_zoo/030_csharp.md) +31. [Challenge project - Create a mini-game](./031_Challenge_mini_game/031_csharp.md)