{"id":14448,"date":"2026-08-05T01:00:00","date_gmt":"2026-08-05T05:00:00","guid":{"rendered":"https:\/\/www.both.org\/?p=14448"},"modified":"2026-08-02T12:46:25","modified_gmt":"2026-08-02T16:46:25","slug":"using-grep-to-play-a-game","status":"publish","type":"post","link":"http:\/\/www.both.org\/?p=14448","title":{"rendered":"Using grep to play a game"},"content":{"rendered":"<div class=\"pld-like-dislike-wrap pld-template-1\">\r\n    <div class=\"pld-like-wrap  pld-common-wrap\">\r\n    <a href=\"javascript:void(0)\" class=\"pld-like-trigger pld-like-dislike-trigger  \" title=\"\" data-post-id=\"14448\" data-trigger-type=\"like\" data-restriction=\"cookie\" data-already-liked=\"0\">\r\n                        <i class=\"fas fa-thumbs-up\"><\/i>\r\n                <\/a>\r\n    <span class=\"pld-like-count-wrap pld-count-wrap\">    <\/span>\r\n<\/div><\/div>\n<p class=\"wp-block-paragraph\">Sometimes I just need to take a break in my work and let my mind relax. And a great way to spend that time is with a word game or word puzzle.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One word game that was quite popular for a time was <a href=\"https:\/\/www.nytimes.com\/games\/wordle\/index.html\">Wordle<\/a>, now hosted at the <em>New York Times<\/em>. I wrote about that a year ago, about how to <a href=\"https:\/\/www.both.org\/?p=5265\">use grep to solve Wordle<\/a> and similar word-guessing puzzles. With a few passes of the <strong>grep<\/strong> command, you can quickly narrow your word guesses from over fifteen thousand to just a few possible words.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I recently discovered another, similar word-guessing puzzle called <a href=\"https:\/\/www.wordiply.com\/\">Wordiply<\/a>, from <em>The Guardian<\/em>. In Wordiply, you have five guesses to write the longest word that includes <em>another, shorter word<\/em>.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"355\" height=\"380\" src=\"http:\/\/www.both.org\/wp-content\/uploads\/2026\/07\/wordiply-start.png\" alt=\"text reads: you have 5 goes to get the longest word that includes the starter word\" class=\"wp-image-14447\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Strings and substrings<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For example, the &#8220;warmup&#8221; game starts with &#8220;her,&#8221; so you need to guess words that have &#8220;her&#8221; in it, such as somew<strong>her<\/strong>e or t<strong>her<\/strong>efore.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"240\" height=\"433\" src=\"http:\/\/www.both.org\/wp-content\/uploads\/2026\/07\/wordiply-her.png\" alt=\"a blue background with the letters H E R in pink at the top, and a &quot;type her&quot; box at the bottom\" class=\"wp-image-14444\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This is another example where <strong>grep<\/strong> can shine. Let&#8217;s use <strong>grep<\/strong> to pick long words with this shorter word inside it. The technical terms that I&#8217;ll use here are <em>string<\/em> and <em>substring<\/em>: the longer word is the <em>string<\/em> and the shorter word within it is the <em>substring<\/em>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, let&#8217;s make a list of all possible words that we can use. Linux stores a list of known words in a file called <code>\/usr\/share\/dict\/words<\/code>, but this list contains &#8220;non-words&#8221; that we don&#8217;t want to use. For example, that list has proper nouns like Minnesota, people names like Aaron, acronyms like USB, and other words that contain hyphens or other punctuation symbols. These words will not be recognized by Wordiply, so we should start with a list of words that we can use.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <strong>grep<\/strong> command uses <em>regular expressions<\/em> to match text. Certain symbols have special meaning, like <code>^<\/code> to match the <em>beginning of a line<\/em>, <code>$<\/code> for the <em>end of a line<\/em>, or <code>*<\/code> to mean <em>zero or more of the character before it<\/em>. Refer to the <a href=\"https:\/\/www.gnu.org\/software\/grep\/manual\/\">grep manual<\/a> for a full list of the regular expressions you can use.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this case, we want to find words that contain only lowercase letters, and nothing else. As a regular expression, we can represent that as <code>^<\/code> for the <em>beginning of a line<\/em>, then <code>[a-z]<\/code> to match any lowercase letter, followed by <code>*<\/code> to make that <em>multiple lowercase letters<\/em>, and finally <code>$<\/code> to match the <em>end of a line<\/em>. That command matches over 355,000 words:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ grep '^&#91;a-z]*$' \/usr\/share\/dict\/words > allwords\n\n$ wc -l allwords\n355537 allwords<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The game starts with &#8220;her&#8221; as the &#8220;inner&#8221; word, so we can use that as the substring to match. This is a simple <strong>grep<\/strong> command to find the substring &#8220;her&#8221; in the list of all words. This reduces the list to just over 4,300 possible words that contain the substring &#8220;her,&#8221; including abolis<strong>her<\/strong> and whit<strong>her<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ grep her allwords &gt; herwords\n\n$ wc -l herwords\n4304 herwords<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">The longest words<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The list is in alphabetical order, because that&#8217;s how the <code>\/usr\/share\/dict\/words<\/code> file started out. The new list has long words and short words mixed together. But the point of the game is to find the longest words that contain &#8220;her.&#8221;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s take the next step with another classic Unix command called <strong>awk<\/strong> to print the length of each word in the list. The <strong>awk<\/strong> language is actually a mini-scripting language, with each instruction as &#8220;condition {instruction}&#8221; pairs. The condition might be a variable comparison like <code>a==z<\/code> for when the <em>a<\/em> variable is equal to the <em>z<\/em> variable, or <code>i&gt;2<\/code> for when the variable <em>i<\/em> is greater than 2, or it could be a regular expression like <code>\/^abc\/<\/code> to match lines that start with the letters &#8220;abc.&#8221; The instruction always goes inside <code>{ }<\/code> curled braces.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Without a condition, <strong>awk<\/strong> will run the instruction for <em>every<\/em> line in the file, which happens to be what we want. To add the length of each word, we can use the <code>length<\/code> function in <strong>awk<\/strong>, which is the length of a word or line. We&#8217;ll also print the word itself at the end, so we can use it later.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ awk '{print length($1), $1}' herwords &gt; herwords.n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This produces a list in <code>herwords.n<\/code> that has the word&#8217;s length, then a space, followed by the word itself. The first 10 entries look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ head herwords.n\n9 abolisher\n10 abolishers\n12 accomplisher\n13 accomplishers\n10 acerathere\n5 acher\n7 acheron\n10 acheronian\n10 acherontic\n11 acidanthera<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">But to find the longest words, we need to sort this list according to length. The <strong>sort<\/strong> command will do just that; <strong>sort<\/strong> is another classic Unix command. On Linux, you can add <code>-n<\/code> to sort as numbers, which will sort the list according to the number up front. Adding the <code>-r<\/code> option will reverse the sort, so the biggest numbers (longest words) will appear at the top of the list.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To make the list easy to view, let&#8217;s send the output to the <strong>less<\/strong> command, which is the standard file viewer on Linux:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sort -nr herwords.n | less<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"907\" height=\"665\" src=\"http:\/\/www.both.org\/wp-content\/uploads\/2026\/07\/wordiply-list.png\" alt=\"a list of words viewed in a terminal emulator on Linux\" class=\"wp-image-14445\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">All that&#8217;s left is to scroll through the list of words and guess words that we <em>think<\/em> will be in the Wordiply dictionary. For example, magnetot<strong>her<\/strong>moelectricity is not recognized by Wordiply, but crystallograp<strong>her<\/strong>s is.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"240\" height=\"433\" src=\"http:\/\/www.both.org\/wp-content\/uploads\/2026\/07\/wordiply-magnetothermoelectricity.png\" alt=\"trying to enter the word magnetothermoelectricity but it's not in their dictionary\" class=\"wp-image-14446\"\/><\/figure>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"240\" height=\"433\" src=\"http:\/\/www.both.org\/wp-content\/uploads\/2026\/07\/wordiply-crystallographers.png\" alt=\"entering the word crystallographers which is in their dictionary\" class=\"wp-image-14443\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">After that, it&#8217;s a matter of scrolling up and down the list to find similarly long words, and making guesses for words that are likely to be in Wordiply&#8217;s dictionary.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Maybe it&#8217;s cheating, but it&#8217;s also an excellent demonstration of how to use <strong>grep<\/strong> and other Linux commands to solve real-world problems. In this case, <strong>grep<\/strong>, <strong>awk<\/strong>, and <strong>sort<\/strong> did the job to find very long words, narrowing the list of possible words from over 355,000 to about 4,300 words, and presenting the list in a way that we can use.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Use grep to help you solve word puzzle games<\/p>\n","protected":false},"author":33,"featured_media":14127,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","footnotes":"","_members_access_role":[],"_members_access_error":""},"categories":[100,69,5],"tags":[104,147,91],"class_list":["post-14448","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-command-line","category-fun","category-linux","tag-command-line","tag-fun","tag-linux"],"modified_by":"Jim Hall","_links":{"self":[{"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/14448","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/users\/33"}],"replies":[{"embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=14448"}],"version-history":[{"count":5,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/14448\/revisions"}],"predecessor-version":[{"id":14506,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/14448\/revisions\/14506"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/media\/14127"}],"wp:attachment":[{"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=14448"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=14448"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=14448"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}