Sunday Puzzle, May 1, 2022

Posted on Updated on

Manipulate a Number Name to get Another number

This week’s challenge is more challenging than last week’s. Write down the name of a number. Move each letter four spots later in the alphabet — so A would become E, B would become F, etc. The result will be a number that’s 44 more than your first one. What numbers are these?

Link to the challenge
Adding 4 to each letter in the number names “one”, “two”, “three”

Discussion

This time I decided to create a WPF solution using Visual Studio. Manipulating the number names was pretty easy, but finding the correct inputs was a lot harder. I had to think of quite a few different ways of representing number names, including Spanish, German, leet and Roman Numerals. Thankfully, I found the solution before I had to resort to Pig-Latin!

This time, instead of explaining the code in text, I made a short movie of the code in action, in a debug session. The reason being that the movie should be easier to understand, in part because I can show the values of variables and demonstrate little blocks of code in the Immediate Pane.

Techniques Used

  • File Handling
  • String manipulation
  • Binary Search
  • Sorting lists in .NET
  • Simple classes
  • StringBuilder
Screen shot showing the solution. Interpretation: when you start with ‘three‘ and move each letter 4 spaces in the alphabet, you arrive at ‘xlvii‘, i.e. the Roman Numeral expression of 47. The difference between 47 and 3 is 44, i.e. this is the solution to the puzzle!

Link to the Video Clip

Hmm, it seems I have to post a link here (instead of directly embedding my video) because I don’t have a Pro account. Here’s my little video! Note that you’ll need a free DropBox account to download it. I hope you learn a couple tricks about using the Visual Studio debugger from watching my video.

Download my Code

Here’s a link to my code. Again, you’ll need a free DropBox account to retrieve that link. My data file of number-names is included in that zip file; the file name is “NumberNames.txt”.

Additional Comments

  • Writing the code to solve the puzzle was pretty easy. The hard part was figuring out all the different ways to represent number names. I’ll step out on a limb and predict that very few people will send a solution to Will this week.
  • One thing I didn’t address in my video is why I bothered to use a Binary search to solve the puzzle, after all, the list of number-names is so short that the speed increase is almost undetectable. In case you wondered why, the answer is:
    1. I’m familiar enough with this technique that it only took me a minute to set up the IComparer class. Compared to Python, binary searches are easy in .NET!
    2. My default is to write code to be efficient, unless doing so makes it hard to read, and Binary Search is quite efficient
    3. I thought someone might be interested to learn how to do this in .NET

Here’s My Code

Since I can’t embed my video directly, here’s the code. First is the “code-behind” for the main from. Below that, I will list my little class to hold instances of NumberNames.

using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Input;

namespace Puzzle2022_05_01 {
    public partial class MainWindow : Window {
        public MainWindow() {
            InitializeComponent();
        }

        /// This method fires when user clicks the 'Solve' button.
        /// 1) Loads a file of number names,
        /// 2) Manipulates each name according to the puzzle instructions. 
        /// 3) If the manipulation results in a
        /// match against another number name, then compute the difference in
        /// the numbers to see if it meets the requirements.
        private void btnSolve_Click(object sender, RoutedEventArgs e) {
            Mouse.OverrideCursor = Cursors.Wait;
            btnSolve.IsEnabled = false;

            var fName = @"C:\Users\User\Documents\Puzzles\NumberNames.txt";
            //1) Build a list 'numberLst' from the number names file:
            var numberLst = LoadNumberNamesFromFile(fName);

            //This object controls which property (field) is used to perform
            //the comparison, namely, the 'Name' property
            var comp = new NumberNameComp();
            //Sorting is necessary to enable the binary search below:
            numberLst.Sort(comp);

            //This list will be bound to the the datagrid which users views
            var displayLst = new List();
            //Now iterate the list, manipulate each name, and
            //search the list for the manipulation result
            foreach(var aNumber in numberLst) {
                //2) Manipulate the original number name:
                var manipulatedName = ManipulateName(aNumber.Name);

                //3) Search the list for an entry matching manipulated name whose
                // difference is 44:
                if (MeetsCriteria(manipulatedName, aNumber.Number, numberLst)) {
                    //Build something to display in the grid:
                    var display = new NumberName {
                        Name = manipulatedName,
                        Orig = aNumber.Name,
                        Difference = 44
                    };
                    displayLst.Add(display);
                }
            }

            lblAnswer.Visibility = Visibility.Visible;
            grdResults.ItemsSource = displayLst;
            grdResults.Visibility = Visibility.Visible;
            Mouse.OverrideCursor = null;
        }

        /// This method opens the file, reads each line, converts the
        /// contents into a NumberName object, and inserts it
        /// into a list to return
        private List LoadNumberNamesFromFile(string fName) {
            var result = new List();
            using (var sr = new StreamReader(File.OpenRead(fName))) {
                while (sr.Peek() != -1) {
                    var aLine = sr.ReadLine();
                    var p = aLine.IndexOf("=");
                    var addMe = new NumberName {
                        Number = long.Parse(aLine.Substring(0, p - 1)),
                        Name = aLine.Substring(p + 2).ToLower()
                    };
                    result.Add(addMe);
                }
            }

            return result;
        }

        /// This method manipulates the name by adding 4 to each letter,
        /// except for hyphens and spaces
        /// For example, 'three' or 'thirteen'
        /// The manipulated name, such as 'xlvii'
        private string ManipulateName(string theName) {
            var result = new StringBuilder();
            foreach (var ltr in theName) {
                //Add 4 unless the character is hyphen or space
                char nextLtr = (ltr == '-' || ltr == ' ')
                    ? ltr
                    : (char)(ltr + 4);
                result.Append(nextLtr);
            }

            return result.ToString();
        }

        /// This method returns true if the manipulated name matches an entry the list,
        /// and if the difference is 44        private bool MeetsCriteria(string manipulatedName, long origNumber, List numberLst) {
            bool result = false;
            var comp = new NumberNameComp();
            var findMe = new NumberName { Name = manipulatedName };
            //3) Searche the
            var ndx = numberLst.BinarySearch(findMe, comp);
            if (ndx >= 0) {
                //If binarysearch succeded, ndx will be non-negative,
                //so compute the difference
                var diff = numberLst[ndx].Number - origNumber;

                //The puzzle criteria demands a difference of 44, this entry 
                //is the solution
                if (diff == 44)
                    result = true;
            }

            return result;
        }
    }
}
    public class NumberName {
        /// <summary>The numeric value of the number, used to compute difference</summary>
        public long Number { get; set; }

        /// <summary>The transformed name of the number, such as xvlii</summary>
        public string Name { get; set; }

        /// <summary>The original number name, such sa 'three'</summary>
        public string Orig { get; set; }

        /// <summary>Holds the difference between the original number and the transformed number</summary>
        public long Difference { get; set; }

        /// <summary>
        /// Displays the number, particularly when using the debugger
        /// </summary>
        /// <returns></returns>
        public override string ToString() {
            return $"{Number}, {Name}";
        }
    }

    /// <summary>
    /// A class dedicated to comparing two instances of the NumberName class
    /// by their name
    /// </summary>
    /// <remarks>
    /// An instance of this class is used for sorting the a list of NumberName
    /// instances, and for performing BinarySearch on such a list
    /// </remarks>
    public class NumberNameComp : IComparer<NumberName> {
        public int Compare(NumberName x, NumberName y) {
            return string.Compare(x.Name, y.Name);
        }
    }

Leave a comment