Hi,
I’m one of those people who always like to learn new things. That’s why I took a closer look at the programming language "Swift 4.1" and app development on the Mac. Since z340 is also a hobby of mine, it was obvious to combine these two. The result is a simple solver that is a combination of Peek-a-boo and AZDecrypt. Since it is not a port of Peek-a-boo, I chose a different name: "Your Secret Pal". A little macabre and ambiguous, I know.
So far I have only implemented the absolute basic functions, but homophonic encryptions can be solved very well. Depending on the settings, z408 takes between 0.17 and 0.7 seconds (iMac 27′ late 2013). I don’t support multithreading at the moment.
The statistics for the Unigram distribution are quite nice, because they update themselves live. If you type something in the left text window, you will see the results immediately. As with peek-a-boo, I attach great importance to visual representations.
The solver is quite robust, but not yet as reliable as AZDecrypt.
My goal is to develop a general cipher tool that focuses not only on homophonic encryption and z340. If I find the time, I’ll add Vigenere and other methods. I just enjoy working on things like that
Here is a screenshot:
Depending on how much time I find, I will release a first version in the next days/weeks. If you are using a Mac and would like to beta test it, please send me a private message and I will send you a preview. That would really help me develop a more stable version.
Translated with http://www.DeepL.com/Translator
It looks good Largo, don’t have a Mac though.
Nice job Largo! I am on Mac and would enjoy playing around with this new solver.
Thanks!
I have added some new features in the last days. However, I don’t want to release today, because the version is not stable enough yet.
However, I’m pretty excited right now, because my experiments with my transposition solver have just delivered some useful results.
To break stacked transpositions, I don’t use a hillclimber, but a genetic algorithm. For a first test I transposed a plaintext in four different steps and then encoded it monoalphabetically. Tests with homophonic encryption will follow as soon as my test results are somewhat better.
Plaintext: First 340 letters of z408 (I LIKE…)
Transposition steps:
– Rotate clockwise
– Transpose Period 3
– Transpose Diagonal 9. This means "Start Top Left, reverse each second diagonal line" (in other words: alternating reading directions)
– Reverse
Cipher:
TTSLTOMLOTTWUTEURSKS SERRSOCAMEASFMAGWQFT TITOFDDSFBCQGEADCGIF MWDOOLSBXODFFASTGTMF TFSMOFTAUFSSWTMYAOKM GOTAZWZOWTAYDAITFTBO RKTOHTCTOUUKKBATSGOL LOAADOHOASODMIOSMOZT MISTMUKFOAMFFYTTMOAM AWFATIDKTMSFTHALAIOL KMOYDTOAKTGSAOHCIUSO TGTGDKITTGZGLTTCUXXS XCAOMMMSKGLZOLHKTGTM TKIDMQUGGSXOTASOMKET IGAKGFLSTOAFLLSBIFIS GUYZTMFYROGMWSMQWSYZ UDLWEIEOELOBQTRMTQOZ
After 19 generations the following solution was found:
EYOUWIGPEOPLILIKEKILLINSSOMUCEBECA USEITIEFUNTHHFUNITIAMORLDGAMEANKIL LINGWITBECAUINTHEFORRESOATDANSEMAN ISTHEMLOFALLGEROUEANAMAHINGGITOKIL LSOMETTTHRILVESMETHEMOACEETISLINGE SPERENHANGETEVENBETTERTKSOFFWTINGY OURROCBESTPAITHAGIRLTHEWHENIRTOFIT IATHAEBORNIDIEIWILLBEREALLTHNPARAD ICEANDWILLBEIHAVEKILLEDSIWILECOMEM YSLAVEYNAMELNOTGIVEYOUMLLTRYBECAUS
That’s far from perfect, but not bad for a first try.
There is still a lot to do…
Translated with http://www.DeepL.com/Translator
Amazing Largo! What is a generation? How many iterations are these?
Largo:
The computer chooses a random path through the message, but with some periocity or pattern? Lots of different paths that are slightly different, and then save the ones with the best scores and combine them somehow? Do another generation, again and again? How does this work?
Jarlve, smokie:
Currently the method is quite simple and the algorithm is rather "naive". So far I have only implemented predefined transposition steps. Smokie, the idea with the paths is very interesting, but also difficult to implement. I’ll give it a try, I like that idea!
That’s how it works at the moment:
A Gene:
A "gene" is simply an integer that represents a certain transposition. Like this:
0 = No transposition
1 = Flip
2 = Mirror
3 = Reverse
4 = Untranspose best Period
5 = Diagonal 1
6 = Diagonal 2
And so on and so forth. Currently I have implemented about 25 transpositions/untranspositions.
Individuals (DNA):
An "individual" is a sequence of the genes shown above, i.e. a DNA strand. The length must be determined first because all individuals have the same length. For example 2, 5, 4, 1 means "Mirror/Diagonal 1/Untranspose best period/Flip"
Population:
A population is a collection of individuals. All individuals of a population are run through for each run. For each individual, the original cipher is transposed using the DNA values. The result is then transferred to the substitution solver. The achieved score is stored in the individual as a fitness value.
Afterwards the next generation is generated. For this, the individuals of the current population are picked out, whereby the fittest are given more preference. These are then crossed with each other (crossover) and mutated on the basis of a probability (the transposition of a gene is randomly exchanged). Thus, a new population is created by simulated genetic inheritance of the old population. This will continue until a defined termination condition is reached. Either a maximum number of generations or a threshold value of the score is reached.
The concept of genetic algorithms is explained in more detail on the following page:
http://www.ai-junkie.com/ga/intro/gat1.html
The drawback is that transpositions can repeat and override each other when the DNA strand is long. For example, 0-2-2-3-3-5 would simply be "Diagonal 1". Another disadvantage is that many similar transpositions can currently be found in the DNA strand directly after each other. For example, 2-5-6 is a rather unlikely solution (mirror, followed by diagonal 1 and diagonal 2). However, here I have an idea that I will implement next:
A gene will get additional information to describe the transposition in more detail. Then, for example, there will only be one gene for "diagonal", which will then contain the reading order as additional information. When mutating, either the entire gene will be exchanged, or only the additional information will be modified. This then gives me the possibility to apply transpositions only to certain areas of the cipher (start and end points are given as additional information).
The difficulty at the moment is to set the parameters in a meaningful way. In the example shown above, each generation has a population size of 150 individuals with a DNA length of 4. The substitution part is solved with a hillclimber and simulated annealing, just like Jarlve does. For a monoalphabetic substitution between 1000 and 5000 iterations are sufficient. For the solution shown above 150*19(generations)*5000 = 14.250.000 iterations were required. With a homophonic substitution this amount increases.
As I said, all this is still in its beginnings and is not yet effective/performant. So far, however, I like the approach. If, for example, Feynman 1 is substituted with homophone substitution, my solver will find a solution. Unfortunately, it takes 1-2 Minutes to get a reasonable solve.
Starting test: Evolve Stacked Transpositions ;aXLdhFoc9nwxlkE:JjA uDq3iC7=y+Y0T1sRYPrX =g2GXAUN5V=KBpaXLqYF EZdxw=2MWXsN37LjyYDH :TYp+=kXlbI=tnGRuev0 PgaXYJ;VUFAqBIw=x73i :jy2p+ZsNFR;0obA12ht dPLHDpuFg;wasnIKlxkX 3=yq+EI20NiPBJpGU7RE XZdg=e;LSVtM:wHckXna FA2xpqDTQsC3GFy+70bN VRQ5=rXP2=LtAloiThI1 qKpXg;nwGVavZSrF=2X= peBbFsx3cMyQtuojUC25 hdYepnF1c2YK7lNXC5Mh opFRl+;Lc0TDIP:AGgqu axVTCY243t=wyQYp7sTi N5+nLhF0PZlwlQ2sd1gN pFKHRJacxDTA2pFq72Ll Generation: 1 Fittest: 13646.7785752307 Reverse, None, Untranspose best period, None WTANTHATIRCALLAWITHH OMETOYBUTMOOTISHASBO CHTYORRACSTHISTRALSY STOSTABOOSYINDWITHES UREEDREDNEINEWICHLIS OFLORWHOSTREESFUNDAN DEUSIMTHERCOMEWHANGE RHOLYTIEDWITHHISTWAY SINBURTTONTRACEGHASH INERALDHOLTINGHIESTT HASINGBACCORREEINGTH EDONDEMONNATISTONSHE LARHASTICRECOMESDCON NEANDERACEROWALETRAD ENRULODDITHATSCURANA LSTANDDHSWITHORANDYM OREIDISHTARNITYBYANT ICCOLICESSHINNYLONDE NROLDSOCOONONRALCEIR Generation: 4 Fittest: 13688.0369016262 None, Rotate Right, Rotate Right, Untranspose best period WSENTHATAPRILREWITHH ICSSOURASCOOTESHADRO GHTEOFTERMSHATSPELME DTOTSAROOSEANDWITHED AWARDWEDNAINSWICHLIM ORLOFWHIMSWERTRANCEN DRADICTHEFLOURWHENCE PHILUSEEMWITHHISSWEE SECRAFTSINSPIRETHATH INAWELDHORTINTHEESST HESENTRACROPPESINTTH ADONCECONNASITSINTHE LETHISSILWECOURSDRON NEENDSTALAFOWALASTAM ENTALODDETHATSLAPENE RTSANDCHSWITHOPANDEC OPRIMESHSATNATUREINS IRCOLAGESTHINNERONCA NFORMSOGOONONPILGRIT Generation: 5 Fittest: 13694.9350762968 Rotate Left, Reverse, Flip, Untranspose best period WHANTHATAPRILLEWITHS INDHOTHESNOOTESSEDHO GSTEOFMARCHSATHPENCE DTOTHEHOOSEANADITSAD EMARTMETRAINDWICHLIC OWNOFWHICHMARTWENGER AREDINTHEFLOURWSANCA PSINTSEEDWITHHISSWEE SETHEFTHINSPIRETHATS INAMENTHOLTINTHEASHT HESENTHECROPPEDIRTTH ATORGANONNEHITHINTHA NAMSISHILMACOURSTRON NEANADMALAFOWELASMAD ARMELOATETHATSLEPENA LTHERTGSSWITSOPENTEN OPRIDESSHEMRATTHEIRH IRCONAGESTSINNELONGA NFOLDSOGOONONPILGRIM
Translated with http://www.DeepL.com/Translator
A new test:
The first 340 characters of z408 were disrupted into 68 parts of 5 characters each. These fragments were randomly rearranged. Example:
Plaintext: THISISASAMPLECIPHERX Fragmented: THISI SASAM PLECI PHERX Fragments shuffled: PLECI SASAM PHERX THISI Fragmented plaintext: PLECISASAMPHERXTHISI
This was then substituted and passed to my solver. With a monoalphabetic substitution the solver manages to create a reasonably readable plaintext. With homophonic encryption it doesn’t work yet. I also don’t know whether the search space isn’t much too large (68 factorial). Let’s see… Either my idea works or I can throw it into the bin
Translated with http://www.DeepL.com/Translator
Nice idea! Hopefully it will work. The search space seems daunting. Isn’t it actually around 68! times 26^63 (if you used 63 symbols in the substitution)?
I think it is roughly equivalent to an untransposed homophonic with a key length of 131. Reasoning: 26^n = 68!*(26^63), then solve for n.
The search space seems daunting. Isn’t it actually around 68! times 26^63 (if you used 63 symbols in the substitution)?
Yes, that’s right. The search space of 68! referred only to the transposition. If homophonic encryption is added, it is extended by the factor 26^63 (for 63 Symbols). With a monoalphabetic substitution the factor is 26!.
I run the test overnight and get only a few fragments of the plaintext. If I didn’t know the plaintext, I wouldn’t be able to tell if the fragments are random or represent parts of the real plaintext. Another problem with this test is the correlation of the fragment size and the nGram size of the solver (both 5). It is no surprise that fragments can be found sometimes.
This test was mainly used to find out how big the search space can be. The fragmentation into 68 parts was purely arbitrary and had no direct connection to z340. Nevertheless, it would have been a big step to solve such a problem.
For now I will extend my general transposition solver. The disadvantage of this solver is that it only works with pre-defined methods (Flip/Diagonal/Reverse/Period etc.). It fails with routes or interlocks. I like Smokie’s idea of letting a route evolve. Unfortunately, I don’t have any idea how to do this yet. If you generate a route similar to the one in the game "Snake", you have to make sure that no paths cross each other. You also have to make sure that as many characters as possible are covered. In addition, one would have to calculate how large the search space is in this case. This is not an easy task either.
Does anyone have any suggestions? I don’t want to give up trying genetic algorithms because there is potential.
Translated with http://www.DeepL.com/Translator
There’s probably an algorithm description somewhere already but I couldn’t find it. However, I went ahead and wrote one in Java just to see if I could. Here’s the result:
package com.zodiackillerciphers.tests; import java.util.ArrayList; import java.util.List; /** * generate all possible routes in a grid, but only return routes that visit all * positions. * * http://zodiackillersite.com/viewtopic.php?p=65066#p65066 * **/ public class GridRoutes { public static void search(int height, int width) { boolean[][] grid = new boolean[height][width]; List<String> visited = new ArrayList<String>(); for (int row=0; row<height; row++) { for (int col=0; col<width; col++) { System.out.println("Starting at (" + row + ", " + col + "):"); search(grid, height, width, row, col, visited); } } } public static void search(boolean[][] grid, int height, int width, int row, int col, List<String> visited) { // bounds check if (row < 0 || row == height || col < 0 || col == width) { return; } if (grid[row][col]) { return; // already visited this position so return (we don't allow routes to intersect // with themselves) } grid[row][col] = true; // add current position to list of visited positions visited.add("(" + row + "," + col + ")"); // if we visited every position, print the route and return if (visited.size() == height * width) { System.out.println(visited); visited.remove(visited.size() - 1); grid[row][col] = false; return; } // visit all neighbors (horizontal and vertical moves only) search(grid, height, width, row, col + 1, visited); search(grid, height, width, row + 1, col, visited); search(grid, height, width, row, col - 1, visited); search(grid, height, width, row - 1, col, visited); visited.remove(visited.size() - 1); grid[row][col] = false; } public static void main(String[] args) { search(3, 4); } }
It starts at each position of the grid with the given dimensions, then generates all possible routes that visit every grid position. Each route is not allowed to cross itself.
Sample output for grid with height 3 and width 4:
Starting at (0, 0): [(0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (1,1), (1,2)] [(0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (2,2), (1,2), (1,1), (2,1), (2,0), (1,0)] [(0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (2,2), (1,2), (1,1), (1,0), (2,0), (2,1)] [(0,0), (0,1), (0,2), (0,3), (1,3), (1,2), (1,1), (1,0), (2,0), (2,1), (2,2), (2,3)] [(0,0), (0,1), (0,2), (1,2), (1,1), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (0,3)] [(0,0), (0,1), (1,1), (1,2), (0,2), (0,3), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0)] [(0,0), (0,1), (1,1), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (1,2), (0,2), (0,3)] [(0,0), (0,1), (1,1), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (0,3), (0,2), (1,2)] [(0,0), (0,1), (1,1), (1,0), (2,0), (2,1), (2,2), (1,2), (0,2), (0,3), (1,3), (2,3)] [(0,0), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (1,2), (1,1), (0,1), (0,2), (0,3)] [(0,0), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (0,3), (0,2), (1,2), (1,1), (0,1)] [(0,0), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (0,3), (0,2), (0,1), (1,1), (1,2)] [(0,0), (1,0), (2,0), (2,1), (2,2), (1,2), (1,1), (0,1), (0,2), (0,3), (1,3), (2,3)] [(0,0), (1,0), (2,0), (2,1), (1,1), (1,2), (2,2), (2,3), (1,3), (0,3), (0,2), (0,1)] [(0,0), (1,0), (2,0), (2,1), (1,1), (0,1), (0,2), (0,3), (1,3), (2,3), (2,2), (1,2)] [(0,0), (1,0), (2,0), (2,1), (1,1), (0,1), (0,2), (0,3), (1,3), (1,2), (2,2), (2,3)] [(0,0), (1,0), (2,0), (2,1), (1,1), (0,1), (0,2), (1,2), (2,2), (2,3), (1,3), (0,3)] Starting at (0, 1): [(0,1), (0,2), (0,3), (1,3), (2,3), (2,2), (1,2), (1,1), (2,1), (2,0), (1,0), (0,0)] [(0,1), (1,1), (1,2), (0,2), (0,3), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (0,0)] [(0,1), (0,0), (1,0), (1,1), (1,2), (0,2), (0,3), (1,3), (2,3), (2,2), (2,1), (2,0)] [(0,1), (0,0), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (0,3), (0,2), (1,2), (1,1)] [(0,1), (0,0), (1,0), (2,0), (2,1), (1,1), (1,2), (2,2), (2,3), (1,3), (0,3), (0,2)] [(0,1), (0,0), (1,0), (2,0), (2,1), (1,1), (1,2), (0,2), (0,3), (1,3), (2,3), (2,2)] Starting at (0, 2): [(0,2), (0,3), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (0,0), (0,1), (1,1), (1,2)] [(0,2), (0,3), (1,3), (2,3), (2,2), (1,2), (1,1), (2,1), (2,0), (1,0), (0,0), (0,1)] [(0,2), (0,3), (1,3), (2,3), (2,2), (1,2), (1,1), (0,1), (0,0), (1,0), (2,0), (2,1)] [(0,2), (0,3), (1,3), (1,2), (1,1), (0,1), (0,0), (1,0), (2,0), (2,1), (2,2), (2,3)] [(0,2), (1,2), (1,1), (0,1), (0,0), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (0,3)] [(0,2), (0,1), (0,0), (1,0), (2,0), (2,1), (1,1), (1,2), (2,2), (2,3), (1,3), (0,3)] Starting at (0, 3): [(0,3), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (1,1), (1,2), (0,2), (0,1), (0,0)] [(0,3), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (0,0), (0,1), (0,2), (1,2), (1,1)] [(0,3), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (0,0), (0,1), (1,1), (1,2), (0,2)] [(0,3), (1,3), (2,3), (2,2), (2,1), (1,1), (1,2), (0,2), (0,1), (0,0), (1,0), (2,0)] [(0,3), (1,3), (2,3), (2,2), (1,2), (1,1), (2,1), (2,0), (1,0), (0,0), (0,1), (0,2)] [(0,3), (1,3), (2,3), (2,2), (1,2), (0,2), (0,1), (1,1), (2,1), (2,0), (1,0), (0,0)] [(0,3), (1,3), (2,3), (2,2), (1,2), (0,2), (0,1), (0,0), (1,0), (1,1), (2,1), (2,0)] [(0,3), (1,3), (2,3), (2,2), (1,2), (0,2), (0,1), (0,0), (1,0), (2,0), (2,1), (1,1)] [(0,3), (0,2), (1,2), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (1,1), (0,1), (0,0)] [(0,3), (0,2), (1,2), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (0,0), (0,1), (1,1)] [(0,3), (0,2), (1,2), (1,3), (2,3), (2,2), (2,1), (1,1), (0,1), (0,0), (1,0), (2,0)] [(0,3), (0,2), (1,2), (1,1), (0,1), (0,0), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3)] [(0,3), (0,2), (0,1), (1,1), (1,2), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (0,0)] [(0,3), (0,2), (0,1), (0,0), (1,0), (1,1), (1,2), (1,3), (2,3), (2,2), (2,1), (2,0)] [(0,3), (0,2), (0,1), (0,0), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (1,2), (1,1)] [(0,3), (0,2), (0,1), (0,0), (1,0), (2,0), (2,1), (1,1), (1,2), (1,3), (2,3), (2,2)] [(0,3), (0,2), (0,1), (0,0), (1,0), (2,0), (2,1), (1,1), (1,2), (2,2), (2,3), (1,3)] Starting at (1, 0): [(1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (0,3), (0,2), (1,2), (1,1), (0,1), (0,0)] [(1,0), (2,0), (2,1), (1,1), (1,2), (2,2), (2,3), (1,3), (0,3), (0,2), (0,1), (0,0)] [(1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (2,2), (1,2), (1,1), (2,1), (2,0)] [(1,0), (0,0), (0,1), (1,1), (1,2), (0,2), (0,3), (1,3), (2,3), (2,2), (2,1), (2,0)] Starting at (1, 1): [(1,1), (1,2), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (0,0), (0,1), (0,2), (0,3)] [(1,1), (1,2), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (2,0), (2,1), (2,2), (2,3)] [(1,1), (1,2), (2,2), (2,3), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (2,0), (2,1)] [(1,1), (1,2), (2,2), (2,1), (2,0), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (2,3)] [(1,1), (1,2), (0,2), (0,3), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (0,0), (0,1)] [(1,1), (1,2), (0,2), (0,1), (0,0), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (0,3)] [(1,1), (2,1), (2,0), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (2,2), (1,2)] [(1,1), (2,1), (2,0), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (1,2), (2,2), (2,3)] [(1,1), (2,1), (2,0), (1,0), (0,0), (0,1), (0,2), (1,2), (2,2), (2,3), (1,3), (0,3)] [(1,1), (0,1), (0,0), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (1,2), (0,2), (0,3)] [(1,1), (0,1), (0,0), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (0,3), (0,2), (1,2)] [(1,1), (0,1), (0,0), (1,0), (2,0), (2,1), (2,2), (1,2), (0,2), (0,3), (1,3), (2,3)] Starting at (1, 2): [(1,2), (2,2), (2,3), (1,3), (0,3), (0,2), (0,1), (1,1), (2,1), (2,0), (1,0), (0,0)] [(1,2), (2,2), (2,3), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (1,1), (2,1), (2,0)] [(1,2), (2,2), (2,3), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (2,0), (2,1), (1,1)] [(1,2), (1,1), (2,1), (2,2), (2,3), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (2,0)] [(1,2), (1,1), (2,1), (2,0), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (2,2)] [(1,2), (1,1), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (0,3), (0,2), (0,1), (0,0)] [(1,2), (1,1), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (2,2), (2,1), (2,0)] [(1,2), (1,1), (0,1), (0,2), (0,3), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (0,0)] [(1,2), (1,1), (0,1), (0,0), (1,0), (2,0), (2,1), (2,2), (2,3), (1,3), (0,3), (0,2)] [(1,2), (0,2), (0,3), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (1,1), (0,1), (0,0)] [(1,2), (0,2), (0,3), (1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (0,0), (0,1), (1,1)] [(1,2), (0,2), (0,3), (1,3), (2,3), (2,2), (2,1), (1,1), (0,1), (0,0), (1,0), (2,0)] Starting at (1, 3): [(1,3), (2,3), (2,2), (2,1), (2,0), (1,0), (0,0), (0,1), (1,1), (1,2), (0,2), (0,3)] [(1,3), (2,3), (2,2), (1,2), (1,1), (2,1), (2,0), (1,0), (0,0), (0,1), (0,2), (0,3)] [(1,3), (0,3), (0,2), (1,2), (1,1), (0,1), (0,0), (1,0), (2,0), (2,1), (2,2), (2,3)] [(1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (2,0), (2,1), (1,1), (1,2), (2,2), (2,3)] Starting at (2, 0): [(2,0), (2,1), (2,2), (2,3), (1,3), (1,2), (1,1), (1,0), (0,0), (0,1), (0,2), (0,3)] [(2,0), (2,1), (2,2), (2,3), (1,3), (0,3), (0,2), (1,2), (1,1), (1,0), (0,0), (0,1)] [(2,0), (2,1), (2,2), (2,3), (1,3), (0,3), (0,2), (1,2), (1,1), (0,1), (0,0), (1,0)] [(2,0), (2,1), (2,2), (2,3), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (1,1), (1,2)] [(2,0), (2,1), (2,2), (1,2), (1,1), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (2,3)] [(2,0), (2,1), (1,1), (1,2), (2,2), (2,3), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0)] [(2,0), (2,1), (1,1), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (2,2), (1,2)] [(2,0), (2,1), (1,1), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (1,2), (2,2), (2,3)] [(2,0), (2,1), (1,1), (1,0), (0,0), (0,1), (0,2), (1,2), (2,2), (2,3), (1,3), (0,3)] [(2,0), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (2,2), (2,1), (1,1), (1,2)] [(2,0), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (2,2), (1,2), (1,1), (2,1)] [(2,0), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (1,2), (1,1), (2,1), (2,2), (2,3)] [(2,0), (1,0), (0,0), (0,1), (0,2), (1,2), (1,1), (2,1), (2,2), (2,3), (1,3), (0,3)] [(2,0), (1,0), (0,0), (0,1), (1,1), (1,2), (0,2), (0,3), (1,3), (2,3), (2,2), (2,1)] [(2,0), (1,0), (0,0), (0,1), (1,1), (2,1), (2,2), (2,3), (1,3), (1,2), (0,2), (0,3)] [(2,0), (1,0), (0,0), (0,1), (1,1), (2,1), (2,2), (2,3), (1,3), (0,3), (0,2), (1,2)] [(2,0), (1,0), (0,0), (0,1), (1,1), (2,1), (2,2), (1,2), (0,2), (0,3), (1,3), (2,3)] Starting at (2, 1): [(2,1), (2,2), (2,3), (1,3), (0,3), (0,2), (1,2), (1,1), (0,1), (0,0), (1,0), (2,0)] [(2,1), (2,0), (1,0), (1,1), (1,2), (2,2), (2,3), (1,3), (0,3), (0,2), (0,1), (0,0)] [(2,1), (2,0), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (2,2), (1,2), (1,1)] [(2,1), (2,0), (1,0), (0,0), (0,1), (1,1), (1,2), (2,2), (2,3), (1,3), (0,3), (0,2)] [(2,1), (2,0), (1,0), (0,0), (0,1), (1,1), (1,2), (0,2), (0,3), (1,3), (2,3), (2,2)] [(2,1), (1,1), (1,2), (2,2), (2,3), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (2,0)] Starting at (2, 2): [(2,2), (2,3), (1,3), (1,2), (1,1), (2,1), (2,0), (1,0), (0,0), (0,1), (0,2), (0,3)] [(2,2), (2,3), (1,3), (0,3), (0,2), (1,2), (1,1), (2,1), (2,0), (1,0), (0,0), (0,1)] [(2,2), (2,3), (1,3), (0,3), (0,2), (1,2), (1,1), (0,1), (0,0), (1,0), (2,0), (2,1)] [(2,2), (2,3), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (2,0), (2,1), (1,1), (1,2)] [(2,2), (2,1), (2,0), (1,0), (0,0), (0,1), (1,1), (1,2), (0,2), (0,3), (1,3), (2,3)] [(2,2), (1,2), (1,1), (2,1), (2,0), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (2,3)] Starting at (2, 3): [(2,3), (2,2), (2,1), (2,0), (1,0), (1,1), (1,2), (1,3), (0,3), (0,2), (0,1), (0,0)] [(2,3), (2,2), (2,1), (2,0), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3), (1,2), (1,1)] [(2,3), (2,2), (2,1), (2,0), (1,0), (0,0), (0,1), (1,1), (1,2), (1,3), (0,3), (0,2)] [(2,3), (2,2), (2,1), (2,0), (1,0), (0,0), (0,1), (1,1), (1,2), (0,2), (0,3), (1,3)] [(2,3), (2,2), (2,1), (1,1), (1,2), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (2,0)] [(2,3), (2,2), (1,2), (1,3), (0,3), (0,2), (0,1), (1,1), (2,1), (2,0), (1,0), (0,0)] [(2,3), (2,2), (1,2), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (1,1), (2,1), (2,0)] [(2,3), (2,2), (1,2), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (2,0), (2,1), (1,1)] [(2,3), (2,2), (1,2), (1,1), (2,1), (2,0), (1,0), (0,0), (0,1), (0,2), (0,3), (1,3)] [(2,3), (1,3), (0,3), (0,2), (1,2), (2,2), (2,1), (2,0), (1,0), (1,1), (0,1), (0,0)] [(2,3), (1,3), (0,3), (0,2), (1,2), (2,2), (2,1), (2,0), (1,0), (0,0), (0,1), (1,1)] [(2,3), (1,3), (0,3), (0,2), (1,2), (2,2), (2,1), (1,1), (0,1), (0,0), (1,0), (2,0)] [(2,3), (1,3), (0,3), (0,2), (1,2), (1,1), (0,1), (0,0), (1,0), (2,0), (2,1), (2,2)] [(2,3), (1,3), (0,3), (0,2), (0,1), (1,1), (1,2), (2,2), (2,1), (2,0), (1,0), (0,0)] [(2,3), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (1,1), (1,2), (2,2), (2,1), (2,0)] [(2,3), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (2,0), (2,1), (2,2), (1,2), (1,1)] [(2,3), (1,3), (0,3), (0,2), (0,1), (0,0), (1,0), (2,0), (2,1), (1,1), (1,2), (2,2)]
There are 124 routes found for that 3×4 grid (if my algorithm is correct). It jumps to 2,012 routes for a 4×5 grid, and 47,391 routes for a 5×6 grid. I’m afraid to run it on 20×17 due to the exponential growth in routes. I have no idea how to explicitly count the search space in advance. Surely some mathematician somewhere has done this exercise before.
Thanks for the explanation Largo. I can see that the genetic algorithm is well suited for the task since it tries out a great variety of things.
The first 340 characters of z408 were disrupted into 68 parts of 5 characters each.
There’s a chance AZdecrypt’s row bound solver (smokie’s concept) can get this if you dimension the cipher into 68 by 5.
Test on the 408 capped at 340 (a bit easier) with the parts randomized does reveal the correct substitution but the parts are still out of order.
Score: 25728.34 Ioc: 0.06021 Ngrams: 273 PC-cycles: 664 Ngram size: 4 ARTOF (422) OUMYN (266) DICES (354) AVESM (351) ETHEM (449) ADIOT (333) YOUWI (405) BECOU (363) ERANC (379) EITAS (386) OODTH (402) AVEKI (352) LBERE (380) ITISS (394) RILLI (395) HINGG (383) COMEM (369) FALLT (404) BESTP (381) ILIKE (439) ANGAR (361) MOODF (330) DHOGI (255) OFFWI (353) SOMET (455) NGEXP (368) LLTRY (379) THEFO (410) LLNOD (295) TUEIN (313) ENIFI (395) ETDER (295) OKILL (406) SEMAN (386) NPARI (340) ILLBE (429) ETTIN (443) AMEBE (375) HANKI (348) GYOUR (443) LLEDW (372) BORNI (363) NGPEO (397) CAUSE (464) ROCKS (374) IMEAN (434) FUNIT (359) ISTHE (462) EVENB (384) GIVEY (390) THAAH (233) RREST (407) THING (486) WILDG (308) ESIWI (333) LLING (450) NFALL (384) CAUSE (464) YSLOV (347) PLEBE (381) AFUND (361) RLTHA (373) KILLA (386) ASMOR (398) OMUCH (434) OMALO (346) HIAWH (281) EAWIL (338)
There’s probably an algorithm description somewhere already but I couldn’t find it. However, I went ahead and wrote one in Java just to see if I could. Here’s the result:
Many thanks for the algorithm, it works very well! I just ported it to Swift 4.1 and experimented a bit with it. But for a 5×6 grid I get 53992 routes. I will check again if I overlooked something while porting.
At 17×3 there are 179840 possible routes just for the start position 0, 0. Again, the search space seems to be way too large. Even with a genetic algorithm that builds the routes on the fly. I don’t see any way to evolve routes. Even if it were a text without substitution.
There’s a chance AZdecrypt’s row bound solver (smokie’s concept) can get this if you dimension the cipher into 68 by 5.
Test on the 408 capped at 340 (a bit easier) with the parts randomized does reveal the correct substitution but the parts are still out of order.
Wow, that’s impressive! I know this feature of AZDecrypt, but I haven’t tried it with such short fragments yet. Amazing how well this works. Here is the AZDecrypt result of my test cipher (59 Symbols).
DYOUR (443) THAGI (335) ILIKE (436) NPARA (358) FUNIT (356) OUMYN (264) THEIS (418) LBERE (378) EVERB (401) OFFWI (351) AMALO (332) NDUNP (307) ISTHO (378) NGALL (401) BORNI (360) OUEAR (328) MOATW (266) HINGD (389) SANKI (307) WILDG (306) LLNOT (400) OATTH (392) ETTER (447) THEFO (407) PLEBE (378) UIWIL (339) ORIGI (402) SOMET (452) ILLBE (426) DIVEY (317) ITIAT (360) ESIWI (331) ARTOF (419) EETIN (394) KILLI (403) UTSOM (410) BECAU (456) AVEKI (349) SEMAN (383) LLTRY (377) CAUSE (461) CAUSE (461) NGPEO (395) URONC (311) RILLI (392) RRENT (430) OMUCH (431) ARDER (399) AMEBE (372) LLING (447) WICEA (371) AMEIN (395) UTTIN (416) HAOWS (184) YOUWI (402) LLEDW (369) RLTHE (371) THARD (400) BESTP (379) YSLAV (320) FALLT (402) IVESM (366) ITINS (377) EFUNT (358) COMEM (366) IAMOR (333) OKILL (403) ROCKS (372)
There’s probably an algorithm description somewhere already but I couldn’t find it. However, I went ahead and wrote one in Java just to see if I could. Here’s the result:
Many thanks for the algorithm, it works very well! I just ported it to Swift 4.1 and experimented a bit with it. But for a 5×6 grid I get 53992 routes. I will check again if I overlooked something while porting.
I re-ran my algorithm and got 53992 routes for 5×6 this time. Maybe I miscounted before.
Thanks for the explanation, Largo. I like your creativity. And Jarlve’s fragment solve above is really amazing.
What about fragments ranging from 1 to some high number, but starting out average 68 fragments of length 5 ( average length of English word ). Each fragment is a gene. The direction is pre-set.
Example cipher: Four, side by side 5 x 17 inscription rectangles, direction of diagonal inscription left right bottom top ( North East ). Then transcribe into a 17 x 20 rectangle left right top bottom and homophonic substitute.
To solve: redraft the message into 20 x 17, set the reading direction to North East, break into fragments. There could be a population of 100, all with different fragments ( genes ). Keep the 50 that get the highest substitution scores and kill the 50 with the lowest scores. Make 25 of the surviving 50 male, and 25 female. Pair them up and breed them, each couple having four children. Not sure how to splice the genes together. Test the new population of 100 and repeat.
Could the program find the borders of the four inscription rectangles?