Sunday Puzzle, May 1, 2022
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
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
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:
- 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! - My default is to write code to be efficient, unless doing so makes it hard to read, and Binary Search is quite efficient
- I thought someone might be interested to learn how to do this in .NET
- I’m familiar enough with this technique that it only took me a minute to set up the
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.
usingSystem.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); } }