Odgadywanie hasła – Zadanie rekrutacyjne #17

Czego szuka rekruter: game loop, walidacja wejścia, komunikaty zwrotne, licznik prób
Poziom: Junior
Zadanie
Napisz grę słowną: program wybiera losowe hasło ze zdefiniowanej listy, gracz odgaduje litera po literze. Program pokazuje odkryte litery (_ _ _ _ _) i informuje o błędach. Gra kończy się gdy gracz odgadnie hasło lub skończy mu się liczba prób.
Czego szuka rekruter
To zadanie o game loop i zarządzaniu stanem gry. Rekruter sprawdza:
- Pętlę gry z jasno zdefiniowanym warunkiem zakończenia
- Stan gry jako zestaw zmiennych (
odkryte litery,zużyte próby,błędne litery) - Walidację wejścia (pojedyncza litera, już użyta litera)
- Czytelne komunikaty dla gracza
Najprostszą wersją jest “zgadywanie litery po literze” (Hangman). Rekruter może rozszerzyć zadanie: “a co gdyby hasło trzeba było podać całe?”.
Modelowanie stanu gry
record GameState(
string Word,
HashSet<char> GuessedLetters, // wszystkie podane litery (trafione + błędne)
int MaxAttempts,
int WrongCount
)
{
public bool IsWon => Word.All(c => GuessedLetters.Contains(char.ToLowerInvariant(c)));
public bool IsLost => WrongCount >= MaxAttempts;
public bool IsOver => IsWon || IsLost;
public string DisplayWord =>
string.Join(" ", Word.Select(c =>
GuessedLetters.Contains(char.ToLowerInvariant(c)) ? c : '_'));
}
✅ Rozwiązanie
static string[] _words = { "programowanie", "komputer", "algorytm", "interfejs", "rekurencja" };
static void PlayGame()
{
var random = new Random();
string word = _words[random.Next(_words.Length)].ToLowerInvariant();
var state = new GameState(
Word: word,
GuessedLetters: new HashSet<char>(),
MaxAttempts: 6,
WrongCount: 0
);
Console.WriteLine($"Hasło ma {word.Length} liter. Masz {state.MaxAttempts} prób.");
while (!state.IsOver)
{
Console.WriteLine($"\n{state.DisplayWord}");
Console.WriteLine($"Błędne litery: {string.Join(", ", state.GuessedLetters.Where(c => !word.Contains(c)))}");
Console.WriteLine($"Pozostałe próby: {state.MaxAttempts - state.WrongCount}");
char guess = ReadSingleLetter(state.GuessedLetters);
state.GuessedLetters.Add(guess);
if (word.Contains(guess))
{
Console.WriteLine($"✓ Litera '{guess}' jest w słowie!");
// State jest rekordem — musimy go odtworzyć z nowym GuessedLetters
// (GuessedLetters to mutable HashSet, więc modyfikujemy bezpośrednio)
}
else
{
state = state with { WrongCount = state.WrongCount + 1 };
Console.WriteLine($"✗ Brak litery '{guess}' w słowie.");
}
}
Console.WriteLine($"\n{state.DisplayWord}");
Console.WriteLine(state.IsWon
? $"Brawo! Odgadłeś '{word}'!"
: $"Przegrałeś. Hasło to: '{word}'.");
}
static char ReadSingleLetter(HashSet<char> usedLetters)
{
while (true)
{
Console.Write("Podaj literę: ");
string? input = Console.ReadLine()?.Trim().ToLowerInvariant();
if (string.IsNullOrEmpty(input) || input.Length != 1)
{
Console.WriteLine("Podaj dokładnie jedną literę.");
continue;
}
char c = input[0];
if (!char.IsLetter(c))
{
Console.WriteLine("Podaj literę (nie cyfrę ani znak specjalny).");
continue;
}
if (usedLetters.Contains(c))
{
Console.WriteLine($"Litera '{c}' była już użyta.");
continue;
}
return c;
}
}
Rozszerzenia do omówienia na rozmowie
Tryb zgadywania całego słowa
static void TryGuessWord(GameState state, string word)
{
Console.Write("Znasz hasło? Wpisz je (lub Enter żeby pominąć): ");
string? attempt = Console.ReadLine()?.Trim().ToLowerInvariant();
if (!string.IsNullOrEmpty(attempt))
{
if (attempt == word)
// wygrał
else
// kara (stracona próba)
}
}
Poziomy trudności
record Difficulty(int MaxAttempts, string[] WordList);
static readonly Difficulty Easy = new(8, new[] { "kot", "pies", "dom" });
static readonly Difficulty Medium = new(6, _words);
static readonly Difficulty Hard = new(4, new[] { "konstantynopolitan", "pszczelarz" });
Testy jednostkowe (logiki gry)
[Fact]
public void GameState_AllLettersGuessed_IsWon()
{
var state = new GameState("abc", new HashSet<char> { 'a', 'b', 'c' }, 6, 0);
Assert.True(state.IsWon);
}
[Fact]
public void GameState_MaxWrong_IsLost()
{
var state = new GameState("abc", new HashSet<char>(), MaxAttempts: 3, WrongCount: 3);
Assert.True(state.IsLost);
}
[Fact]
public void GameState_DisplayWord_ShowsGuessedLetters()
{
var state = new GameState("abc", new HashSet<char> { 'a', 'c' }, 6, 0);
Assert.Equal("a _ c", state.DisplayWord);
}
Co powiedzieć na rozmowie
“Game loop jest prosta: dopóki gra nie jest zakończona — pokaż stan, pobierz wejście, zaktualizuj stan. Kluczowe rozdzielenie: GameState przechowuje dane, ReadSingleLetter obsługuje wejście z walidacją, PlayGame koordynuje całość. Dzięki temu logika gry jest testowalna niezależnie od Console. HashSet<char> dla użytych liter daje O(1) sprawdzanie czy litera była już użyta.”
Podsumowanie
Odgadywanie hasła testuje umiejętność modelowania stanu i pisania czytelnego game loop. Kluczowe wzorce: while (!state.IsOver), separacja ReadInput od logiki gry, HashSet dla O(1) sprawdzania użytych liter.
Zamień wiedzę w umiejętności
Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.
Pobieram roadmapę →