Acrostic Squares

Dec 06, 2005

The Acrostic Square is a square which contains the same words across as down. The canonical example:

 
        SATOR
        AREPO
        TENET
        OPERA
        ROTAS
        

Recently in the news is the old problem of trying to come up with a 10x10 square, the discovery of which is said to grant one a "lifetime of immortality"[sic]. The news article is actually about the work of Ted Clarke from 1999. His solution:

 
        DISCUSSING
        INCANTATOR
        SCARLATINA
        CARNITINES
        UNLIKENESS
        STATESWREN
        SATINWEAVE
        ITINERATES
        NONESEVENT
        GRASSNESTS
        

This solution, like other stabs at a 10 square, is a wee on the iffy side, seeing as how certain words (e.g. nonesevent, stateswren) are not, how shall we say, words. Be that as it may, I thought this looked like a perfect problem for a python script to strangle. Using a set of word lists and a python script, I can safely say that the 10 worder is outside of the bounds of most viable English. Even that 20,302 word long list contains no set of words that can form a viable square.

Using the wordox lists, I was able to calculate many thousands of 5x5 and 6x6 squares (I didn't count them all), 10,501 7x7 squares, and 5 8x8 squares. The 8x8's:

 
        c a r b o r a s
        a p e r i e n t
        r e c a l l e r
        b r a s s i c a
        o i l s e e d s
        r e l i e v o s
        a n e c d o t e
        s t r a s s e s
        ------
        c r a b w i s e
        r a t l i n e s
        a t l a n t e s
        b l a s t e m a
        w i n t e r l y
        i n t e r t i e
        s e e m l i e r
        e s s a y e r s
        ------
        n e r e i d e s
        e n e r g i s e
        r e s o n a t e
        e r o t i z e d
        i g n i t e r s
        d i a z e p a m
        e s t e r a s e
        s e e d s m e n
        ------
        n e r e i d e s
        e t e r n i s e
        r e l o c a t e
        e r o t i z e d
        i n c i t e r s
        d i a z e p a m
        e s t e r a s e
        s e e d s m e n
        ------
        n e r e i d e s
        e t e r n i s e
        r e n o v a t e
        e r o t i z e d
        i n v i t e r s
        d i a z e p a m
        e s t e r a s e
        s e e d s m e n
        ------
        

These, admittedly, are not very satisfying, as their words are substantially odd. However, they are officially recognized words for both Scrabble and Wordox, so I stand by 'em. My script found that there were no larger squares possible in the available words.

Here is the script. To run it, simply save it to a file, and call it from the command line with a wordlist filename as the only argument. The wordlist file should have one word per line in like case (either all lower or all upper).

Toggle line numbers
   1 
           2 class AcrosticSquare:
           3     def __init__(self, wordlistfile):
           4         self.wordlist = [word.strip() for word in open(wordlistfile).read().split('\n')]
           5         self.size = len(self.wordlist[0])
           6 
           7         # the number of unique letters in the acrostic square
           8         self.limit = (self.size * self.size + self.size) / 2
           9 
          10         """ The planned arrangement of data in self.letters (the numbers are the 'index'):
          11         0  1  2  3  4
          12         1  5  6  7  8
          13         2  6  9  10 11
          14         3  7  10 12 13
          15         4  8  11 13 14
          16         """
          17         self.letters = [ [ None for i in xrange(self.size) ] for i in xrange(self.size) ]
          18         self.options = [ [ None for i in xrange(self.size) ] for i in xrange(self.size) ]
          19         self.lettermap = {}
          20 
          21         self.index2coords = {}
          22 
          23         # Build a map for convenient getting of coordinates
          24         row = -1
          25         rowsum = 0
          26         for i in xrange(self.limit):
          27             if (i + rowsum) % self.size == 0:
          28                 row += 1
          29                 rowsum = sum(range(row + 1))
          30             col = (rowsum + i) % self.size
          31             self.index2coords[i] = (col, row)
          32 
          33         # Build the letter tree
          34         for word in self.wordlist:
          35             struct = self.lettermap
          36             for letter in word:
          37                 if letter not in struct:
          38                     struct[letter] = {}
          39                 struct = struct[letter]
          40 
          41     def report(self, answer):
          42         if answer != None:
          43             for x in answer:
          44                 for a in x:
          45                     print a,
          46                 print
          47             print "------"
          48 
          49     def run(self, index = 0):
          50         if index == self.limit:
          51             return self.letters
          52 
          53         col, row = self.index2coords[index]
          54 
          55         if col > 0:
          56             colChoices = self.options[col - 1][row][0]
          57         else:
          58             colChoices = self.lettermap
          59         if row > 0:
          60             rowChoices = self.options[col][row - 1][1]
          61         else:
          62             rowChoices = self.lettermap
          63 
          64         for letter in colChoices:
          65             if letter in rowChoices:
          66                 self.letters[col][row] = letter
          67                 self.letters[row][col] = letter
          68                 self.options[col][row] = [ colChoices[letter], rowChoices[letter] ]
          69                 self.options[row][col] = [ rowChoices[letter], colChoices[letter] ]
          70 
          71                 result = self.run( index + 1)
          72                 self.report(result)
          73 
          74         return None
          75 
          76 #----------------- runnit
          77 import sys
          78 try:
          79     file = sys.argv[1]
          80 except IndexError:
          81     print "Please specify a word list to use."
          82     sys.exit(1)
          83 
          84 a = AcrosticSquare(file)
          85 a.run()