Question Advanced guessing game - Code review

GravityCat190

New member
Joined
Nov 23, 2020
Messages
1
Programming Experience
Beginner
Hello :3
I'm learning C# and after watching the course and get the basics I decide to write a small project. It's a guessing game where a player can:
  1. guess (of course): that will subtract his/her energy
  2. rest: that will recovery his/her energy but take out some time
  3. take a hint: get a hint about the current puzzle but it takes out some time
  4. dice roll: get random a positive or a negative effect
It does all the tasks it has to do, it's all about code review. I will be grateful for all suggestions for optimization of solutions/other solutions, good practices (writing, commenting on the code) and everything else that you think may help me in the future.

PS: As I wrote above, it's my first C# project. Before, I wrote some simple and small projects in other technologies/languages.

It contains: Main program, Hero class, Puzzle class
Code -> GravityCat190/Advanced-Guessing-Game
 
First of all, never upload the obj directory into github. The same way you never upload the final build output bin directory, you shouldn't upload the obj directory which contains the intermediate results of compiling/linking/building.

This is pointless if all you are trying to do is show that you don't care about the command line parameters:
C#:
static void Main(string[] args)
{
    Start();
}

static void Start()
{
    : // main loop here
}

Just do remove the parameters and put the code from Start() inside Main():

C#:
static void Main()
{
    : // main loop here
}

In modern coding style, the preference is self-documenting code. The less comments the better because the code should speak for itself. So something like this:
C#:
static void StartNewGame()
{
    // Introduction
    Console.WriteLine("You started a new game");
    Console.WriteLine("...");
    Console.WriteLine("...");
    Console.WriteLine("...");
    Console.WriteLine("Hello Hero! You forgot to wear your plot armor and some random snake bites you. You will die in 24 hours.");
    Console.WriteLine("Yea well that's all. Good luck!");

    bool gameOver = false;
    while (gameOver == false)
    {
        : // loop body
    }
}

should look more like this:
C#:
static void Introduction()
{
    Console.WriteLine("You started a new game");
    Console.WriteLine("...");
    Console.WriteLine("...");
    Console.WriteLine("...");
    Console.WriteLine("Hello Hero! You forgot to wear your plot armor and some random snake bites you. You will die in 24 hours.");
    Console.WriteLine("Yea well that's all. Good luck!");   
}

static void StartNewGame()
{
    Introduction();

    bool gameOver = false;
    while (gameOver == false)
    {
        : // loop body
    }
}

C# prefers Allman indent style. If you have your own indent style then apply it consistently. So this:
C#:
if (Hero.Alive == false) { gameOver = true; }
should be written as:
C#:
if (Hero.Alive == false)
{
    gameOver = true;
}

Don't use magic numbers. The fact that you have to explain what the magic number means is more comments that you have to write:
C#:
// If player solves a puzzle, whichPuzzle is incremented. If WhichPuzzle returns 4 it means that the player solved all of the puzzles
if (Puzzle.WhichPuzzle == 4) { gameOver = true; }
Use constants and/or enums:
C#:
static class Puzzle
{
    public const int LastPuzzle = 4;
    :
}

:

gameOver |= Puzzle.WhichPuzzle == Puzzle.LastPuzzle;

Why two dimensional array of hints in the Puzzle class?

Don't use exceptions for flow control:
C#:
int validCharsCounter = 0;
// playerGuess.Length-1 -> I'll use i to go through the entire array. The returned length will be greater than the last index
for (int i = 0; i <= playerGuess.Length-1; i++)
{
    try
    {
        if (currentAnswer[i] == playerGuess[i]) { validCharsCounter++; }
    }
    catch (IndexOutOfRangeException)
    {
        break;
    }
}
return validCharsCounter;
Do the right thing and figure out how to avoid the exception:
C#:
for (int i = 0; i < Math.Min(playerGuess.Length, currentAnswer.Length); i++)
{
    if (playerGuess[i] == currentAnswer[i])
    {
        validCharsCounter++;
    }
}

This could be simplified:
C#:
// if validCharsCounter was equal to 0 before being modified, after modifying it may be equal to -1
if (validCharsCounter == -1) { validCharsCounter = 0; }
with
C#:
validCharsCounter = Math.Max(validCharsCounter, 0);

Instead of having a public WhichPuzzle and a backing whichPuzzle where your intent was for whichPuzzle to only be updatable within the class, you could have just done this instead
C#:
public static int WhichPuzzle { get; private set; }

The fact that you have to comment which status index means which:
C#:
private static int[] statusesCounters = // hero doesn't have any active status at the beginning
{
    0, // AHA Moment - Player will get hint about answer's length
    0, // Motivation - extra energy after guessing the puzzle
    0, // Narcolepsy - chance that rest will not give you energy
    0, // Effective antidote - more time after guessing the puzzle
    0, // Slow moves - resting and taking hint will take more time
    0, // Dizziness - failed guessing may return a modified result of valid chars
    0, // Exhaustion - resting will give only half energy
    0  // Student syndrome - resting and taking hint will take the maximum possible amount of time
};

and have all kind of magic numbers to recall which status is which suggest that you really should have a class (or struct) that looks like:
C#:
class Status
{
    public int AhaMoment { get; set; }
    public int Motivation { get; set; }
    public int Narcolepsy { get; set; }
    public int EffectiveAntidote { get; set; }
    public int SlowMoves { get; set; }
    public int Dizziness { get; set; }
    public int Exhaustion { get; set; }
    public int StudentSyndrome { get; set; }
};

That's it for now, the thing I've been waiting for at work has occurred. Back to work for me.
 
Back
Top Bottom