Solving Problems on HackerRank
Solving problems on HackerRank is a fantastic way to keep your coding skills sharp. With the right environment setup and a structured approach, you can solve problems efficiently and effectively. In this guide, I’ll walk you through my environment setup and demonstrate my approach to solving the staircase problem using Test-Driven Development (TDD) in C#.
Environment Setup
The following instructions assume you’ll be using C# with Visual Studio Code. Let's get everything set up!
Step 1 - Install .NET SDK
Note: Skip this step if you already have the .NET SDK installed.
First, head over to Microsoft’s .NET SDK for Visual Studio Code to install the SDK. Then, verify your installation with the following command:
dotnet --info
You should see output similar to this:
Note: The output may differ depending on your operating system.
❯ dotnet --info
.NET SDK:
Version: 8.0.108
Commit: 665a05cea7
Workload version: 8.0.100-manifests.109ff937
...
# content truncated for readability
Step 2 - Create a .NET Solution and Test Project
Next, let's create a .NET solution for the problem:
mkdir Staircase
cd Staircase
dotnet new sln -n Staircase
Then, create a test project and add it to the solution:
dotnet new xunit -n Staircase.Tests
dotnet sln add Staircase.Tests
Finally, build and test the solution to ensure everything is set up correctly:
dotnet build
dotnet test
If all goes well, your test project should build and run successfully.
Step 3 - Run Tests with Hot Reload
To make your problem-solving process faster, use this command in your solution folder:
dotnet watch test --project Staircase.Tests
This enables hot reloading, so your tests automatically run whenever you make changes, providing instant feedback. 🚀
The Problem-Solving Process
My approach to solving HackerRank problems involves Test-Driven Development (TDD). Here’s a brief overview of TDD and how I apply it.
A Bit of Theory
TDD is guided by three core laws:
- Write a failing test before writing any production code.
- Write only enough test code to fail, or fail to compile.
- Write only enough production code to make the failing test pass.
These laws guide the Red/Green/Refactor cycle:
- Create a unit test that fails. 🔴
- Write production code to make the test pass. 🟢
- Refactor the code to clean up any mess. ✨
For a deeper dive into TDD, I highly recommend reading The Cycles of TDD by Uncle Bob.
Problem-Solving in Action
Now, let's apply this process to the staircase problem.
In your Staircase.Tests
project:
- Create a
StaircaseTests.cs
file for your tests. - Run
dotnet watch test --project Staircase.Tests
in your terminal.
First Cycle
Let's start with the case when the staircase should be EmptyWhenNoStairs
.
🔴 Create a Unit Test That Fails
Write a unit test that fails in the StaircaseTests.cs
file:
[Fact]
public void EmptyWhenNoStairs()
{
Assert.True(string.IsNullOrEmpty(Result.staircase(0)));
}
Your terminal should show output like this:
dotnet watch 🚀 Started
Determining projects to restore...
All projects are up-to-date for restore.
/path/to/staircase/Staircase.Tests/StaircaseTests.cs(10,42): error CS0103: The name 'Result' does not exist in the current context [/path/to/staircase/Staircase.Tests/Staircase.Tests.csproj]
dotnet watch ❌ Exited with error code 1
dotnet watch ⏳ Waiting for a file to change before restarting dotnet...
🟢 Write Production Code to Make the Test Pass
Create the Result
class and the staircase
method to fix the failing test:
internal class Result
{
internal static string staircase(int n)
{
return string.Empty;
}
}
✨ Refactor the Code to Clean Up Any Mess
In this step, there’s no mess to clean up, so let’s move on.
Yay! We’ve solved one case, and our production code works! 🎉
Second Cycle
Now, let's move on to the next case: FirstLevelHasOneStair
.
🔴 Create a Unit Test That Fails
[Fact]
public void FirstLevelHasOneStair()
{
Assert.Equal("#", Result.staircase(1));
}
dotnet watch test
should show that this test is failing, which is exactly what we want.
🟢 Write Production Code to Make the Test Pass
Update the staircase
method to make the test pass:
public static string staircase(int n)
{
if (n == 1) return "#";
return string.Empty;
}
Your terminal should now display:
Passed! - Failed: 0, Passed: 2, Skipped: 0, Total: 2, Duration: 3 ms - Staircase.Tests.dll (net8.0)
dotnet watch ⌚ Exited
dotnet watch ⏳ Waiting for a file to change before restarting dotnet...
✨ Refactor the Code to Clean Up Any Mess
Again, there’s no need for cleanup at this point, but always make it a habit to check for any mess that needs tidying up.
⚠️ Remember, test code is as important as production code. Keep it clean!
Next Cycles
By now, you’ve got the hang of it. Think about the next small step or the next unit test that should fail.
Here are all the steps I followed, each representing a TDD cycle:
[Fact]
public void EmptyWhenNoStairs()
{
Assert.True(string.IsNullOrEmpty(Result.staircase(0)));
}
[Fact]
public void FirstLevelHasOneStair()
{
Assert.Equal("#", Result.staircase(1));
}
[Fact]
public void HasTheGivenNumberOfStairs()
{
var stairs = Result.staircase(10).Split('\n');
Assert.Equal(10, stairs.Length);
}
[Fact]
public void EachStairHasTheMaximumWidth()
{
var stairs = Result.staircase(10).Split('\n');
foreach (var stair in stairs)
{
Assert.Equal(10, stair.Length);
}
}
[Fact]
public void EachStairEndsWithHash()
{
var stairs = Result.staircase(10).Split('\n');
foreach (var stair in stairs)
{
Assert.Equal('#', stair[10 - 1]);
}
}
[Fact]
public void EachStairHasTheCorrespondingNumberOfHashes()
{
var stairs = Result.staircase(10).Split('\n');
for (int i = 0; i < 10; i++)
{
var expectedNoOfHashes = i + 1;
var actualNoOfHases = stairs[i].ToCharArray().Count(c => c == '#');
Assert.Equal(expectedNoOfHashes, actualNoOfHases);
}
}
And here’s my final solution for the staircase
problem:
internal class Result
{
internal static string staircase(int n)
{
var result = new StringBuilder();
for (int currentLevel = 1; currentLevel <= n; currentLevel++)
{
result.Append(BuildStair(currentLevel, n));
}
return result.ToString();
}
private static string BuildStair(int currentLevel, int noOfLevels)
{
var hashes = "#".PadLeft(currentLevel, '#');
var stair = hashes.PadLeft(noOfLevels, ' ');
if (currentLevel != noOfLevels)
stair += "\n";
return stair;
}
}
Conclusion
By setting up your environment correctly and following a disciplined approach like TDD, you can tackle HackerRank challenges efficiently. Each small step in TDD, no matter how trivial, builds up to a robust and well-tested solution. The key is to move slowly and methodically—small, deliberate steps will ultimately lead to faster, more reliable problem-solving. Happy coding! 😄
Top comments (0)