Wordle Helper 2

Mar 23, 2022

My previous Wordle helper has now been supplanted by a newer variant, which adds a number of nice new features. Here's a screenshot of the new UI:

Wordle Helper 2

The top five rows represent the slots for each character of the five-letter word. Each row consists of four controls:

  • The left-most column displays the possible remaining choices for that position.
  • The column with a red background is where you specify letters that are in the word, but not in the given position.
  • The column with a green background is where you specify letters that are in the word and are in the given position.
  • The final column shows the subset of possibilities using only the letters known to be in the word.

As long as the body of the page has focus, you may simply type letters to remove them from play. Holding shift while typing a letter will add that letter to the pool of known letters. Holding the control key will remove the letter from the pool (if you make a mistake). Be careful with this, however; some browser shortcuts cannot reliably be trapped (ctrl + w being one of them).

As an alternative to typing, you can click the on-screen keyboard to remove letters, and Shift + click letters to add them. Here's a second screenshot showing the tool while in use:

Wordle Helper 2 (in use)

Date Nut Balls

Mar 13, 2022

This family favorite is a staple of the holiday season. It's one of my all-time favorite snacks, and you truly cannot eat just one!

  • 1 stick butter (or margarine)
  • 1 cup light brown sugar
  • 8 oz. package of dates, cut fine
  • 4 oz. flaky coconut
  • 1 cup chopped nuts (peanuts or walnuts)
  • 2 cups Rice Krispies
  • Powdered sugar

Combine the butter, brown sugar, dates, and coconut in a heavy saucepan. Start a timer when you put it on the stove and cook for 6 minutes. Do not overcook!

After the timer sounds, add the chopped nuts and the Rice Krispies, mix well, and let it cool. Shape the mixture into balls, and roll in powdered sugar while still warm.

Where possible, my family tries to reduce the amount of plastic we consume. One example of this is our choosing not to purchase tubs of spreadable butter, a food staple we eat plenty of. Instead, we convert sticks of butter (which come in paper containers) into spreadable butter. The recipe we use to do this is shown below. It's an easy way to prevent extra plastics from entering the waste stream.

  • Two sticks unsalted butter (room temperature)
  • 1/2 tsp salt
  • 2/3 cup oil

Bring the sticks of butter to room temperature by leaving them out overnight. Place the butter into a bowl, along with the salt and oil of your choosing. We use avocado oil, as it has a pretty neutral taste. Olive oil is also a good choice, but be aware that olive oil can have a fairly overpowering taste.

Mix the ingredients well with a hand-mixer. Pour the contents into a container (we reuse old, plastic butter tubs) and refrigerate. Two sticks of butter fit nicely into a 15-ounce butter container.

I love Sublime Text and use it across a number of my computers. Occasionally, one of my systems becomes down-level, especially in terms of the packages I'm using. I know I can sync my user profiles via Git (or something similar), but occasionally it's good to reset things and start from a fresh profile.

So that I can keep track of what I primarily use, I'm writing down the packages I use on my primary development system.

Last Updated: December 23, 2024

Wordle Helper

Feb 23, 2022
Note that this has been replaced by a newer version.

I've been enjoying Wordle, despite the transition to The New York Times (I expect the game to disappear behind the paywall sometime in the near future). Some of the variants of Wordle have also been enjoyable, my favorite among them being Quordle.

One strategy I've used as I've played over the past few weeks is to use a text-editor to keep track of the letters I know are valid, along with their positions. This aids me in figuring out potential words to guess, and others to rule out. Being the programmer that I am, I turned this manual process into a tiny, self-contained web application:

Wordle Helper Screenshot

You enter the characters you know are present in the word in the top box. As you learn about the positions of each character, you also fill in that information. In the example shown above, the word I'm trying to guess is "power." I know the o is in the second position, and I know the w is not in the first position. The resulting choices set at the bottom of each column helps me figure out what it might be.

It was fun to put together this tiny little script.

I don't open multiple file handles at once very often in Python, so it surprised me to find out this morning that you have to use the old-school line continuation hack to make it work (in 3.9 or earlier):

# Note the trailing backslash on the next line...
with open('a.txt', 'w') as file_a, \
     open('b.txt', 'w') as file_b:

    # Do something with file_a
    # Do something with file_b

Happily, Python 3.10 fixes this by adding Parenthesized Context Managers, which allows us to use parentheses like you would expect to:

# Only in Python 3.10+
with (
    open('a.txt', 'w') as file_a,
    open('b.txt', 'w') as file_b
):

    # Do something with file_a
    # Do something with file_b

The project I'm working on is still on Python 3.9, but it's good to know this was improved, and is a motivator to upgrade the version I'm using.

Flying Blind

Feb 6, 2022

I love almost any video that Tom Scott puts out. His latest, from his Tom Scott Plus channel, is fantastic. The teaser: "She's blind. I'm blindfolded. We're going to fly a plane." Check it out!

Every Day Meatloaf

Feb 1, 2022

This is a recipe I've been meaning to transcribe for a while now. It's delicious and the sauce is what really makes it (we sometimes double the sauce recipe).

  • 2 slices of dry bread, broken
  • 1 cup milk
  • 1.5 pounds ground beef
  • 2 beaten eggs
  • 1 onion, diced
  • 1 tsp. salt
  • 1/2 tsp. sage
  • Dash of pepper

Soak bread in milk while assembling the other ingredients. Add meat, eggs, onion, sage, salt, and pepper to a bowl; mix well. Place in baking pan and cover with piquant sauce. Bake in a moderate oven (350 degrees F) for 45 minutes to 1 hour.

Piquant Sauce

  • 3 Tbsp. brown sugar
  • 1/4 cup ketchup
  • 1/4 tsp. nutmeg
  • 1 tsp. dry mustard

Combine ingredients in a small bowl.

This new Technology Connections video is great, and has me wanting to buy a new can opener.

Circus Peanuts

Jan 14, 2022

I am one of the few people on this planet who enjoys circus peanuts (though, given that multiple companies manufacture them, I can't be alone, right?). In fact, I bought a small bag of them tonight at the grocery store. My wife, who dislikes them immensely, tried a bit of one to remind herself of their taste. As expected, she still hated it. "It tastes like styrofoam," she said. I guess that means there's more for me!

Circus peanuts; yum!

Anderson Bruford Wakeman Howe were a progressive rock band comprising 4 of the 5 members of Yes. They only released a single, self-titled studio album, and it might as well be considered a "lost" Yes album. Released in 1989, well after the 90125 album that redefined their sound, this particular album is really nice. It has a number of great melodies, terrific musicianship, and varied musical themes. Give it a listen here on YouTube (I apparently can't embed the video, so the link will have to suffice).

I like using Google Photos, as it has nearly all of the features I care about. However, the search capability is puzzlingly lacking, especially from a company whose core foundation is based on searching for stuff. One of the features that Google touts for Photos is their AI-enhanced searching capability. For instance, if you type a color into the search box, you'll see a listing of photos that feature that color; pretty neat! However, if you search for specific text that appears in your image descriptions, it won't provide any results.

One of things I tend to do for my wildlife pictures is enter the animal's identification into the image description: for example, the description for a picture of a male northern cardinal might look something like Northern Cardinal (Male). Later, if I happen to search for "northern cardinal," no results will be returned! I'd love to be able to search the meta-data (and maybe even the comments) for my photos. It would allow me to use the photos as a database of sorts, which is as it should be.

I Hate FedEx

Dec 28, 2021

A little over a month ago, I ordered an UpLift Desk for my office. All three packages were shipped via FedEx, and it was a colossal headache. The top arrived damaged, so I had the company send me another, which they graciously did. That one also arrived slightly damaged, though not as badly as the first. I decided to use the second top, just to avoid having to deal with another support ticket (the desk, by the way, is terrific).

The FedEx tracking updates, while packages are en route, appear to be updated only once daily. Compare this to UPS or Amazon, who update their tracking information immediately. FedEx's estimations for when the packages would arrive were also all incorrect (only one of the three packages ended up arriving the day it was forecast).

Nearly a week ago, my wife's phone broke. We ordered a replacement direct from Google, and guess who it's shipping through? We ordered it December 22, and it was scheduled to be delivered December 26, by the end of the day. The last tracking update was December 23; nothing further appeared until this morning. Today's update shows that the package is in Charlotte, NC, but with this caveat: Delivery exception: Damaged, handling per shipper instructions.

I guess at least they're honest. It's not clear to me, however, what happens next. Will FedEx deliver the damaged package as is? Will it get returned to the shipper, only to have a new one shipped again? I contacted Google about it, but my support ticket is still being processed (apparently the Google support folks don't even know what happens).

FedEx has miles to go to catch up to their competition. I've never had these kind of issues with UPS or Amazon shipping. It amazes me that a company with this kind of service remains in business.

Gmail Spam Filtering

Dec 21, 2021

Is it just me, or has Gmail's spam filtering gotten worse? In the past month or so, I've had more false positives end up in the spam folder than I've ever had while using their service. Emails from all sorts of valid places:

  • My online pharmacy orders
  • Electronic receipts from The Home Depot
  • Bug reports on GitHub / GitLab
  • Notifications from NewsBlur, my RSS feed reader of choice

Every time one of these false positives occurs, I click the "Not Spam" button, but it doesn't seem to make much of a difference. Has anyone else seen anything similar? It's really rather frustrating.

Musical Output

Dec 12, 2021

I haven't yet seen the Get Back documentary about The Beatles, but I've been watching various clips of it online. One of the surprising things about what I've seen so far is the rejection of some of George Harrison's songs: specifically All Things Must Pass, which is one of George's best all-time compositions. Like many of his other songs, however, John and Paul dismiss it.

I genuinely don't understand this mindset. My best guess is that there's a lot of ego going into these decisions (and likely jealousy). As a member of a band, whose very existence relies on making music, shouldn't the group welcome every single song or idea a member comes up with? If I were in a band, I feel like I'd want to embrace every idea that came along. More ideas lead to more songs; that, in turn, leads to more albums, more sales, and more touring possibilities. It seems like a no-brainer to me. Think of all the great music the world has been denied because of petty squabbles between people in a musical group. What a shame.

How Optical Mice Work

Dec 4, 2021

I had a general idea about how optical mice work, but I didn't know the specifics. This nicely animated video gives a short, high-level overview of exactly what's happening. It's remarkably well produced.

Christmas Price Index

Nov 17, 2021

In my recent sleep-deprived state, I started to think about the Christmas song The Twelve Days of Christmas. Being a cumulative song, I've always found it funny that, technically speaking, the gift-giver ends up giving the true love 364 gifts. I got looking at the Wikipedia page for the song, and lo and behold, there's an actual economic indicator (albeit a tongue-in-cheek one) that PNC Wealth Management maintains about these gifts. It's called the Christmas Price Index, and gets used as a commodity price index. More amusing than the index itself are some of the criticisms of it that are listed in the Wikipedia article:

First, the index does not clearly define the products that comprise each of the twelve gifts. For example, the price for the eight "maids a-milking" only includes the cost of eight laborers at Federal minimum wage, while milking also requires at least a milk cow, goat, or other such animal, which is an additional cost.

Even better is this note on the index for 2020:

The 2020 index did not include nine Ladies Dancing, ten Lords-A-Leaping, eleven Pipers Piping, or twelve Drummers Drumming due to COVID-19 restrictions on live performances.

Brilliant.

CSV Parsing Woes

Nov 14, 2021

An occasional annoyance of my job is having to deal with poorly constructed data. One recent instance of this came through a collection of CSV files. In these files, certain free-form text fields sometimes included either non-escaped double quotes or an embedded newline where there shouldn't be one. Shortened examples of each are shown below:

"Samsung","ABC-12345","2.5 TB SAS 2.5" hard drive","Released","2018-06-01"
"Lenovo","DEF-88776 
PQR-66554","Mechanical chassis","Released","2020-02-22"

The first line above has an embedded double quote character which has not been escaped. The second line showcases a rogue newline character.

Parsing these problematic cases in Python gets real tricky, and the native csv module doesn't have great malformed data handling support. While thinking about how to handle these situations, it occurred to me that I could use the way the file was constructed to my advantage. These files are output by, what is to me, a black box. Under the hood it's undoubtedly a database query, the results of which are then sent into a CSV format. As a byproduct, each file has a consistent format where each field is quoted, and fields are separated by a comma. I can use the "," string (double quote, comma, double quote) as my separator, looking for the fields I expect:

previous_chunk = []
with open(infile, 'r', encoding='utf8') as csvfile:
    with open(f"{infile.stem}-clean.csv", 'w', encoding='utf8', newline='') as outfile:
        writer = csv.writer(outfile, quoting=csv.QUOTE_ALL)

        for line in csvfile.readlines():
            line = line.rstrip()  # Trim the trailing newline

            pieces = line.split('","')  # Split on our separator
            pieces[0] = re.sub(r'^"', '', pieces[0])  # Remove the first double quote
            pieces[-1] = re.sub(r'"$', '', pieces[-1])  # Remove the last double quote

            # If we don't have the number of columns we expect, merge
            if len(pieces) != expected_columns:
                previous_chunk = merge_chunks(previous_chunk, pieces)
                if len(previous_chunk) == expected_columns:
                    writer.writerow(previous_chunk)
                    previous_chunk = []
                elif len(previous_chunk) > expected_columns:
                    print(f"ERROR: Overran column count! Expected {expected_columns}, Found "
                          f"{len(previous_chunk)}")
            else:
                writer.writerow(pieces)

The merge_chunks method is very simple:

def merge_chunks(a, b):
"""
Merges lists a and b. The content of the first element of list b will be appended
to the content of the last element of list a. The result will be returned.
"""
    temp = []
    temp.extend(a)

    if a:
        temp[-1] = f"{a[-1]} {b[0]}"
        temp.extend(b[1:])
    else:
        temp.extend(b)

    return temp

I believe the only way this could potentially break is if the content, for some reason, contained the "," separator somewhere in a data field. Given the types of data fields I'm working with, this is highly unlikely. Even if it does occur, I can use the format of some of the fields to make best guesses as to where the actual dividers are (e.g. the trailing elements on each line are typically always date stamps).

This is obviously not a general solution, but it sometimes pays to step away from the built-in parsing capability in a language and roll your own scheme.

I recently had to change the URLs of some of my REST endpoints in a project at work. In so doing, I started receiving reports of users who weren't seeing the data they expected from some endpoints. My redirect seemed simple enough:

location ~* ^/rest/(report/.*)$ {
    return 302 https://$host/rest/project/$1;
}

So, I'm sending URLs like https://myhostname/rest/report/1/ to https://myhostname/rest/project/report/1/. The redirect worked, but the problem was that any additional query string that was included in the URL got dropped.

For example, https://myhostname/rest/report/1/?query=somevalue would result in https://myhostname/rest/project/report/1/. The fix for this is easy, but is something I didn't realize you had to pay attention to:

location ~* ^/rest/(report/.*)$ {
    return 302 https://$host/rest/project/$1$is_args$args;
}

The $is_args directive will return a ? if there are arguments, or an empty string otherwise. Similarly, $args will return any arguments that happened to be passed. Including these variables ensures that any query parameters get passed along with the redirect.

Here's another terrific Tom Scott video, this time showcasing a shooting range in Switzerland where you shoot over a busy road. The technology behind the electronic targets is pretty neat. One more reason why Switzerland is one of the best places on Earth!