Avatar of isavegas

isavegas's solution

to Hangman in the C# Track

Published at Sep 24 2018 · 0 comments
Instructions
Test suite
Solution

Note:

This exercise has changed since this solution was written.

Implement the logic of the hangman game using functional reactive programming.

Hangman is a simple word guessing game.

Functional Reactive Programming is a way to write interactive programs. It differs from the usual perspective in that instead of saying "when the button is pressed increment the counter", you write "the value of the counter is the sum of the number of times the button is pressed."

Implement the basic logic behind hangman using functional reactive programming. You'll need to install an FRP library for this, this will be described in the language/track specific files of the exercise.

Hints

This exercise requires you to work with events. For more information, see [this page] (https://docs.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/events/) .

Running the tests

To run the tests, run the command dotnet test from within the exercise directory.

Further information

For more detailed information about the C# track, including how to get help if you're having trouble, please visit the exercism.io C# language page.

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

HangmanTest.cs

using Xunit;

public class HangmanTest
{
    [Fact]
    public void Initially_9_failures_are_allowed()
    {
        var game = new HangmanGame("foo");

        HangmanState lastState = null;
        game.StateChanged += (sender, state) => lastState = state;

        game.Start();

        Assert.Equal(HangmanStatus.Busy, lastState.Status);
        Assert.Equal(9, lastState.RemainingGuesses);
    }

    [Fact(Skip = "Remove to run test")]
    public void Initially_no_letters_are_guessed()
    {
        var game = new HangmanGame("foo");
        
        HangmanState lastState = null;
        game.StateChanged += (sender, state) => lastState = state;

        game.Start();

        Assert.Equal("___", lastState.MaskedWord);
    }

    [Fact(Skip = "Remove to run test")]
    public void After_10_failures_the_game_is_over()
    {
        var game = new HangmanGame("foo");
        
        HangmanState lastState = null;
        game.StateChanged += (sender, state) => lastState = state;

        game.Start();

        for (var i = 0; i < 10; i++)
        {
            game.Guess('x');
        }

        Assert.Equal(HangmanStatus.Lose, lastState.Status);
    }

    [Fact(Skip = "Remove to run test")]
    public void Feeding_a_correct_letter_removes_underscores()
    {
        var game = new HangmanGame("foobar");

        HangmanState lastState = null;
        game.StateChanged += (sender, state) => lastState = state;

        game.Start();

        game.Guess('b');

        Assert.Equal(HangmanStatus.Busy, lastState.Status);
        Assert.Equal(9, lastState.RemainingGuesses);
        Assert.Equal("___b__", lastState.MaskedWord);

        game.Guess('o');

        Assert.Equal(HangmanStatus.Busy, lastState.Status);
        Assert.Equal(9, lastState.RemainingGuesses);
        Assert.Equal("_oob__", lastState.MaskedWord);
    }

    [Fact(Skip = "Remove to run test")]
    public void Feeding_a_correct_letter_twice_counts_as_a_failure()
    {
        var game = new HangmanGame("foobar");
        
        HangmanState lastState = null;
        game.StateChanged += (sender, state) => lastState = state;

        game.Start();

        game.Guess('b');

        Assert.Equal(HangmanStatus.Busy, lastState.Status);
        Assert.Equal(9, lastState.RemainingGuesses);
        Assert.Equal("___b__", lastState.MaskedWord);

        game.Guess('b');

        Assert.Equal(HangmanStatus.Busy, lastState.Status);
        Assert.Equal(8, lastState.RemainingGuesses);
        Assert.Equal("___b__", lastState.MaskedWord);
    }

    [Fact(Skip = "Remove to run test")]
    public void Getting_all_the_letters_right_makes_for_a_win()
    {
        var game = new HangmanGame("hello");
        
        HangmanState lastState = null;
        game.StateChanged += (sender, state) => lastState = state;

        game.Start();

        game.Guess('b');

        Assert.Equal(HangmanStatus.Busy, lastState.Status);
        Assert.Equal(8, lastState.RemainingGuesses);
        Assert.Equal("_____", lastState.MaskedWord);

        game.Guess('e');

        Assert.Equal(HangmanStatus.Busy, lastState.Status);
        Assert.Equal(8, lastState.RemainingGuesses);
        Assert.Equal("_e___", lastState.MaskedWord);

        game.Guess('l');

        Assert.Equal(HangmanStatus.Busy, lastState.Status);
        Assert.Equal(8, lastState.RemainingGuesses);
        Assert.Equal("_ell_", lastState.MaskedWord);

        game.Guess('o');

        Assert.Equal(HangmanStatus.Busy, lastState.Status);
        Assert.Equal(8, lastState.RemainingGuesses);
        Assert.Equal("_ello", lastState.MaskedWord);

        game.Guess('h');

        Assert.Equal(HangmanStatus.Win, lastState.Status);
        Assert.Equal("hello", lastState.MaskedWord);
    }
}
using System;
using System.Collections.Generic;

public class HangmanState {
    public HangmanStatus Status { get; set; }
    public int RemainingGuesses { get; set; }
    public string MaskedWord { get; set; }
    public HashSet<char> Guesses { get; set; }
}

public enum HangmanStatus {
    Busy,
    Win,
    Lose
}

public class HangmanGame {
    public HangmanState State { get; set; }
    private string HiddenWord;

    public HangmanGame(string word) {
        HiddenWord = word;
        State = new HangmanState();
        State.Guesses = new HashSet<char>();
        State.RemainingGuesses = 9;
        State.MaskedWord = new String('_', HiddenWord.Length);
    }

    public void Start() {
        State.Status = HangmanStatus.Busy;
        StateChanged(this, State);
    }

    public void Guess(char c) {
        if (!State.Guesses.Contains(c)) {
            State.Guesses.Add(c);
            if (HiddenWord.Contains(c)) {
                int i = -1;
                char[] maskedWord = State.MaskedWord.ToCharArray();
                while (true) {
                    i = HiddenWord.IndexOf(c, i+1);
                    if (i > -1) {
                        maskedWord[i] = c;
                    } else {
                        break;
                    }
                }
                State.MaskedWord = new string(maskedWord);
                if (State.MaskedWord == HiddenWord) {
                    State.Status = HangmanStatus.Win;
                    StateChanged(this, State);
                }
                return;
            }
        }

        State.RemainingGuesses -= 1;

        if (State.RemainingGuesses <= 0) {
            State.Status = HangmanStatus.Lose;
        }
    }

   public delegate void EventHandler<T>(HangmanGame game, T t);

   public event EventHandler<HangmanState> StateChanged;
}

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?