DecryptR–A Glass House Application

 

Periodically, I like to build a “glass house” application – that is one Decrypter2which I document as I go, hiding nothing and showing all the thinking that goes into the application as well as the raw, not ready-for-production code along the way.

DecryptR

DecryptR is a game, but one which will illustrate a number of Mango features.  It will not be written in XNA, but rather will be written in Xaml and C#. 

The idea of the game is that the computer thinks of a code, using n of the first m letters of the alphabet (where n and m are configurable).  Thus, we might decide that it will use 6 of the first 8 letters (A-H).  The code does not duplicate letters, so a typical code might be ACBFED but of course the code is not displayed.  Your job is to break the code.  Each “turn” you put in 6 letters and Decrypter tells you how many you matched, and how many were in the right position. 

Thus, if the code is ACBFED and you put in BCAFDH the response would be 5,1, because five of your letters are in the code (BCAFD) and one (F) is in the right position.

Eventually, we will make a beautiful UI for this game, allowing the user to pick letters from tiles, and to have the tiles animate as they appear, with the results displayed nicely.  For now, we want to make do with the simplest UI that will work.

What? No MVVM?

Another glass house application that I’m working on with Jon Galloway (as part of the Full Stack Project) is being written test-first and with MVVM.  I’ve decided to write this  application in the get it working and keep it working design pattern, making use of code-behind rather than creating VMs. 

YAGNI

The idea of get it working and keep it working is to create something simple that works, and then to progressively refine it and to add features as needed.  Get it working and keep it working also subscribes to YAGNI – You Ain’t Gonna’ Need It, which says not to add a feature until the last possible minute, as you may never need it after all.

The Core

Step 1 in get it working and keep it working is to build just enough UI to be able to put in guesses and get results.  That requires

  • A TextBox to enter each guess
  • A Button to say “accept and score this guess”
  • A List Box to show all the guesses and their scores

Here’s the Xaml for the content panel,

<Grid x:Name="ContentGrid"
      Grid.Row="1">
   <Grid.RowDefinitions>
      <RowDefinition
         Height="80" />
      <RowDefinition
         Height="*" />
   </Grid.RowDefinitions>
   <StackPanel
      Orientation="Horizontal">
      <TextBox
         Name="Entry"
         Height="Auto"
         Width="150"
         Grid.Row="0"
         HorizontalAlignment="Left" />
      <Button
         Name="Go"
         Content="Go"
         Margin="5,0,0,0" />
   </StackPanel>
   <ListBox
      Name="Results"
      HorizontalAlignment="Stretch"
      VerticalAlignment="Stretch"
      Margin="5"
      Grid.Row="1"/>
</Grid>

 

The basic application consists of two major areas of functionality:

  • Setting up the code using a random number generator
  • Scoring the user’s guesses

Setting Up The Code

To get started, we need a number of members and properties.  For example, we need a collection to hold the correct answer

private List<char> answer = new List<char>();

We also need a collection of the guesses that we can set as the ItemsSource of the list box.  Rather than fussing with INotifyPropertyChanged, we can make the collection and ObservableCollection and notification is handled automagically.

public ObservableCollection<string> InterimResults = 
    new ObservableCollection<string>();

Finally, we need a number of constants that we’ll implement as properties so that we can, eventually, configure these from a configuration page (and then from isolated storage) [YAGNI alert!]

public int Answer_Size { get; set; }
public int Number_Of_Letters { get; set; }
private List<char> alphabet = new List<char> 
   { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 
     'I', 'J', 'K', 'L', 'M' };

The constants are initialized in the constructor, where a call is also made to GenerateAnswer() – the method responsible for generating the code and stashing it away in the answer member.

private string GenerateAnswer()
{
    Random r = new Random();
    StringBuilder sb = new StringBuilder();
    for (;;)
    {
        int alphaOffset = r.Next( Number_Of_Letters );
        if (answer.Contains( alphabet[alphaOffset] ))
            continue;
       
        // sb.Append( '*' );
        sb.Append( alphabet[alphaOffset] );

        answer.Add( alphabet[alphaOffset] );
        if (answer.Count == Answer_Size)
            break;
    }
    sb.Append("\t\t?\t?");
    return sb.ToString();
}

We begin by creating an instance of Random, which will generate random numbers, and a StringBuilder to build up the answer string. We then iterate in a forever loop, randomly generating offsets of 0 through the Number of Letters that we’re using as our answer pool (minus one due to the zero offset).

As we get unique letters, we add them to the answer, and we add them to the StringBuilder.  Note that right now, while we’re creating the project, we’re displaying the code on the first line, but once we are ready to play we’ll comment that out and use the asterisk instead. 

Here’s the complete constructor, for context:

public MainPage()
{
    InitializeComponent();
    // TODO: Get from configuration
    Answer_Size = 6;
    Number_Of_Letters = 8;
    Duplicates = false;  //YAGNI?

    string initialRow = GenerateAnswer();
    InterimResults.Add( initialRow );
    Results.ItemsSource = InterimResults;

    Go.Click += new System.Windows.RoutedEventHandler( Go_Click );
}

Notice that the string returned from GenerateAnswer becomes the first string in the collection, and the collection is set as the ItemsSource for the list box.

The only remaining work is to set up the event handler on the Go Button.

Go Button Event Handler

When the user clicks Go we have a guess, which we then need to compare to the answer.  Our first job is to make sure that the guess is not null and that there are exactly n characters in the guess,

void Go_Click( object sender, System.Windows.RoutedEventArgs e )
{
    string guess = Entry.Text.ToUpper();
    Entry.Text = string.Empty;
    if (string.IsNullOrEmpty( guess ) || 
           guess.Length != Answer_Size)
    {
        MessageBox.Show( 
           "Must be " + Answer_Size.ToString() + " characters." );
        return;
    }

We’re now ready to count the matches.  We do this with an outer loop (looping over each guess) and an inner loop (looping over each letter in the answer).  If the letter is found, we have a match; if the offsets are the same then we also match position.

int totalFound = 0;
int totalPosition = 0;

for (int i = 0; i < Answer_Size; i++)
{
    for (int j = 0; j < Answer_Size; j++)
    {
        if (guess[i] == answer[j])
        {
            totalFound++;

            if (i == j)
            {
                totalPosition++;
            }
            continue;
        }
    }
}

with that in hand, we’re ready to form the string that will be put into the list box, and if all six were found in the right position, we congratulate the player,

string interimResult = 
    guess + 
    "\t\t" + 
    totalFound.ToString() + 
    "\t" + 
    totalPosition.ToString();
InterimResults.Add( interimResult );

if (totalFound == totalPosition 
    && totalFound == Answer_Size)
{
    MessageBox.Show( "You win!" );
}

We’re done.  There are plenty of features to add, and much work to do on the UI, but this is now an entirely playable game.  Change GenerateAnswer to hide the code, and give it a whirl.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in Essentials, Mango, Mini-Tutorial, Patterns & Skills and tagged . Bookmark the permalink.

5 Responses to DecryptR–A Glass House Application

Comments are closed.