Link to home
Start Free TrialLog in
Avatar of James Elliott
James ElliottFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Anagram Solver?

Hi all.

I am currently trying to put together a small access program that will untangle an anagram.
I already have imported a LARGE dictionary file with over 1 million entries. It is very simplistic with a single entry on each line.

I know there are probably loads of ways to do this but I finally decided on using filters on a form. First to exclude all entries that are not of the relevant length.
 
me.filter = "len(dict) = len(field1)"

I then thought that for each letter of the anagram I would add another filter layer that would exclude all those entries without the relevant letters. I got a certain way but couldn't cope with problem of repeat letters. Sysntax was also a problem due to there being only one filter command.

So my questions are.

Is there a way to apply to filters to the same set of data on a form.

Am I going about this the wrong way?

If not then can someone suggest how I might overcome the problem of duplicate letters?

Any other comments/suggestions are encouraged.

Points will be awarded to anyone with any in depth thoughts.

Thanks

Jell

Avatar of Nosterdamus
Nosterdamus
Flag of Israel image

Hi jell,

No hard feeling indeed....

Please post some examples of the problem, it sounds like a nice head breaking one...

Nosterdamus
Avatar of Paurths
Paurths

hi jell,

how about using the (total) ascii value?

u would need an extra column in your table where the ascii value of the word is stored.

then calculate the value of any given word,
and filter on that value.

perhaps adding up isn't a good idea, but multipliying should produce good values.

just a quick thought...
Avatar of James Elliott

ASKER

I'll post some examples in a sec.

Paurths, nice thought.

However, wouldn't we end up with more that one answer. The dictionary I have is actually a password hacking dictionary. And trust me it's huge. The are many combinations of the same words. (e.g example, eaxmple, exampel etc.)

Could your Ascii method be adjusted to acommodate this.

Thanks

Jell

Still open to other methods.


Build an index of the letters that makes the word.

Example:

Gear and Rage use the same index: aegr

what you have to do is build a sorted index of all your words and then show them according to their index relationship.

Just a thought...

;-)

Nosterdamus
correction:
Gear and Rage use the same index: aegr

should read:
Gear and Rage use the same index KEY:aegr
Another good thought. Thanks.

If poss, I would like to do this as 'programmaticaly' as possible. Ideally I would like one table(dictionary), one form with two fields and a button. This is probably not realistic but I would rather not start making queries and index tables etc.

However, I am prepared to use whatever works. Simplicity is just a preference.

Thanks

Jell

Examples on the way

I've used this to go through each letter in turn of the anagram:

Dim charnum As Integer
Dim strvar As String

Text0.SetFocus
strvar = Text0.Text
charnum = Len(strvar)

For i = 1 To charnum
strvar = Left(Text0.Text, i)
strvar = Right(strvar, 1)
MsgBox "" & strvar
Next i

I then tried to cocantenate the filter function to read something like

me.filter = "instr(1,field1," & str & ") <> 0" etc.

This is were I was getting annoyed with there being only one filter. I was trying to use the next i funtion above to cocantenate a string which I could use a big filter criteria using the AND operator.

As you can imagine the syntax was a nightmare.

Thanks

Jell
Ok jell, try the following little function and see if it suits your need...

Public Function BuildTheIndex(strWord As String) As String
    Dim i As Integer
    Dim j As Integer
    Dim strArr() As String
    Dim strTemp As String
   
    ReDim strArr(Len(strWord))
    For i = 1 To Len(strWord)
        strArr(i) = LCase(Mid(strWord, i, 1))
    Next i
   
    For i = 1 To UBound(strArr)
        For j = i + 1 To UBound(strArr)
            If strArr(i) > strArr(j) Then
                strTemp = strArr(i)
                strArr(i) = strArr(j)
                strArr(j) = strTemp
            End If
        Next j
    Next i
   
    strTemp = ""
    For i = 1 To UBound(strArr)
        strTemp = strTemp & strArr(i)
    Next i
   
    BuildTheIndex = strTemp
   
End Function


Public Sub test()
    Const MyText = "This Is A Sample"
    Dim ReturnResult As String
   
    ReturnResult = BuildTheIndex(MyText)
   
    MsgBox "Original string = " & MyText & vbCrLf & vbCrLf & _
           "IndexKey string = " & ReturnResult
End Sub

You can run the test subroutine to see how it works.

Now, add a new field to your table (MyIndex???) and set it to indexed (allow duplicates). Create an update query to build your index:

Update MyTable Set MyIndex = BuildTheIndex(MyField)

This will create the index.

In your form, on the UfterApdate event of your textbox, place:

Me.Filter = "[MyIndex] = BuildTheIndex(Me.MyTextBox)"


HTH,

Nosterdamus
Correction:
In your form, on the UfterApdate event of your textbox, place:

Should realy read:
In your form, on the AfterUpdate event of your textbox, place:

;-)

Nosterdamus
Hold all comments while I test it.

Thanks

Jell
ok, here's a wierd idea...

what if you (one time job) add another column to your db of words that counts the number of letters using a 26-character length string such as:

{abcdefghijklmnopqrstuvwxyz}

bob  = {02000000000000100000000000}
tom = {00000000000010100001000000}
bill = {01000000100200000000000000}
     
this will take a while i'd assume as you'd process a recordset, but once it's done, it'll be DONE... know what i mean?

then you take your anagram and add up the count of each letter and you find the matching prhase...

such as: libl (=bill) = {01000000100200000000000000}

then you do a select from your word table where the result matches that new colum to find all the values that match, then you select from that result set...

adding ascii values won't work, though it's a good idea (and my original thought! :)) because a word with a length of 3 and an ascii value of 330 could be : "nnn", "mno", "lnp" "ttb" etc...

i don't have a clue of how you'd extend this to multiple words... it's just an idea! :)

dovholuk
Just a suggestion,

Take your dictionary and index it by each word in an alphabetize anagram. I.e. dictionary = acdiinorty

Then alphabetize your anagrams you wish to solve and look up matches in dictionary.
I now you said "Hold all comments" but I found another error that should be corrected:

Me.Filter = "[MyIndex] = BuildTheIndex(Me.MyTextBox)"

should be:
Me.Filter = "[MyIndex] = " & BuildTheIndex(Me.MyTextBox) & ";"
sorry about "no more posts", i was still typing...

but now i'm wondering...

is this going to extend to more than one word? if so, take the word: teammate... scramble it into mmeettaa... does this "unscramble" to team mate or to teammate? how does the computer know? does it display all possibilities like the "what does your phone number spell" sites?

just wondering...

dovholuk
Nosterdamus

Tried it but couldn't get the filter to work.

I've done the update query (which took 20mins BTW!) but I can't seem to inegrate this into my form.

Say I have two fields: InputF and OutputF

InputF is obviously where I will enter the anagram. OutputF is where I want my answer(s). Does OutputF have the index field or the word field as it's control source?

Which field does your AfterUpdate go.

Can you write me some new script around the above. I would like the code to go on a button that will process the anagram.

Thanks

Dovholuk -

Great idea. Would have any advantages over the method I'm trying to use at the moment?

In the future I want to expand this by getting it to solve an anagram puzzle. I'e. how many words of 5 - 9 letters can be found. etc.

Any thoughts?

At the moment I'm not bothered if the function returns a couple of results (e.g. team mate or teammate.)

Thanks

Jell
sorry for bad spelling and gramma - I'm English

jell, what is the option you're "currently" using? do you mean the filter method? if that's what you mean, there's a big advantage.

first, i'd still check the length of the string in question (i'm talking ONE word only here btw.) then i'd match all the strings (using a query) that pulled out the same COUNT of letters. After that, i'd select (From that list) which word matches exactly... the code for this would be CONSIDERABLY simple... such as:

if we have the ANAGRAMMED WORD "llib" (bill of course)

all we'd need to do is this ONE query:

SELECT
     *
FROM
     MyDictionaryTable
WHERE
     len(DictionaryWord) = len("llib") AND
     DictionaryLetterCount = "01000000100200000000000000"

this should match ALL the words in the dictionary that fit (refer to my question above about "teammate") and should produce the answer. which very well MIGHT be more than one word... bear, and bare are two other good examples of anagramed words that'll cause "problems"...

some notes:
i would put the length of each word into the table, as well as the "LetterCount" so  your dictionary table would look like:

DictionaryWord          : WordLength       : LetterCount

make sense?

this way, all the "complicated code" wouldn't be necessary, no filters nothing...

mind you, i've NOT tried it. (but this is "fun") all this stuff i'm putting out here is just "theoretical"

if you want me to give it a go i can... (just not while at work!!! ;-) ) i'll try when i get home ( 3hrs+ from now )

does that help any / make sense to anyone??? :-)

dovholuk
Sounds like a good idea.

It's something I'd like to develope myself over the next few weeks after trying a few ideas. If you could do a bit of testing on the LetterCount idea then I would be gratefull. (V.Clever BTW!)

I'll leave the thread open for a bit and then I'll assign points.

Thanks for the great response.

Jell

further thought...

if you have more than 9 of any one letter, you might want to use hex (so 15 letters instead of 9) or double the numbers (00=a, 01=b, 00=c, 02=d etc.) if your string is REALLY long or has a lot of the same letters... 9 letters is a lot until you start getting into phrases... then 9 letters might go quick... know what i mean?

dovholuk
Good point.

Nosterdamus - I'm going to give you the 200 on the provision that you help me with the small filter problem.

dovholuk - I'll post a new question for 100 points - great ideas!

Csimpson/Paurths - I'll post a question fot 50 points each. Thanks for your help.

Thanks all, great thread!

Watch this space.

Regards

Jell
Hi jell,

The idea that I had in mind is as follows:

Make two forms (Master/Child):
frmMaster will contain two fields:
InputF: TextBox (where you attach the AfterUpdate routine to)
fldTmpIndex: TextBox with it's visible property set to NO!
frmMaster will NOT be bounded to any table/field, as it is just the method to enter your input.

frmChild will be set as a continues form and will contain:
OutputF: TextBox bound to the fldWord in your table
MyIndex: TextBox bound to the index field with it's visibility property set to NO!

The data source of frmChild will be your table.

The relationship between the two forms will be:
frmMaster.fldTmpIndex <-> frmChild.MyIndex


After you enter any word in frmMaster.InputField and hit Enter/Tab (or you may rather create a button for this purpose), a routine should be executed that will do something like the follows:

Private Sub InputF_AfterUpdate()
    Me.fldTmpIndex.Value = BuildTheIndex(Me.InputF.Value)
    Me.Requery
End Sub

This will populate all the words that have the same index, as the one that makes the word in InputF, in the child form...


How does this sounds as a solution?

Nosterdamus
Hi jell,

Sorry for the multiple posts but after I wrote the my prev comment, I realized that it could not be achieved, as you are not allowed to make a relationship between unbound forms.

therefor, the following adjustments to the above should be made:

frmMaster will contain two fields:
InputF: TextBox (where you attach the AfterUpdate routine to) UNBOUND
fldTmpIndex: TextBox with it's visible property set to NO! and BOUND to field Index in your table.

frmMaster will BE bounded to your table.

The after update routine should look like:
Private Sub InputF_AfterUpdate()
    Me.Filter = "[MyIndex] = '" & BuildTheIndex(Me.InputF.Value) & "'"
    Me.FilterOn = True
End Sub

All other instructions are the same.

HTH,

Nosterdamus
Hi Nostre.

I'm sorry but I just can't follow what you are asking me to do. I printed of the entire question and started a new .mdb affresh.

Can you please post a new comment. Telling me exactly what to do please. Many thanks. I'm having problems with the following.

1. <The relationship between the two forms will be:
frmMaster.fldTmpIndex <-> frmChild.MyIndex>

How do I accomplish this?

2. Private Sub InputF_AfterUpdate()
   Me.Filter = "[MyIndex] = '" & BuildTheIndex(Me.InputF.Value) & "'"
   Me.FilterOn = True
End Sub

  or


Private Sub InputF_AfterUpdate()
   Me.fldTmpIndex.Value = BuildTheIndex(Me.InputF.Value)
   Me.Requery
End Sub


which one is it?

Thanks for your help but I'm getting myself a little confused I think.

Thanks

Jell
Hi jell,

1. Create a table (tWords) with two fields:
   fldWord as text
   fldIndex as Text, Indexed allow duplicates.

2. Create an update query to update fldIndex with BuildTheIndex routine (I guess you already accomplished that).

3. Make two forms:
     a. frmMaster:
          Forms RecordSource: tWoprds
          Insert two text boxes into frmMaster:
             1. InputF as text. This field should stay unbound.
                   In the AfterUpdate event of this field, put the following routine:
                   Private Sub InputF_AfterUpdate()
                       Me.Filter = "[MyIndex] = '" & BuildTheIndex(Me.InputF.Value) & "'"
                       Me.FilterOn = True
                   End Sub
             2. TheIndex as Text. This field should be bounded (ControlSource) to fldIndex.
                   Visible property set to NO

     b. frmChild:
          Forms RecordSource: tWoprds
          Set the form view to continues, Vertical scrollbar, no div lines, no navigation bars.
          Insert two text boxes into frmChild:
             1. fldWord as text. This field should be bounded to fldWord in tWords.
             2. fldIndex as text. This field should be bounded to fldIndex in tWords.

4. Open frmMaster in design view. from the controls bar, select insert subform/subreport.
      SourceObject: frmChild
      Link child field: fldIndex (from tWords).
      Link Master field: fldIndex (from tWords).

5. Show frmMaster in FormView and enter a word in fldWord (press Enter or Tab when done)...


Note that the index should exist prior to form activation.


HTH,

Nosterdamus

p.s. I truely hope that the identation is OK....
Hi Nostre.

Thanks for the great explanation. However, it still doesn't like the filter line on the after_update function.

It prompts me for a value for [MyIndex]. I figured that this was because the field is no longer called MyIndex but 'fldindex' instead. so I tried that.

Now it asked me for the value of whatever anagram I enter.

Please help coz I think I'm nearly there.

Thanks in advance

Regards

Jell
Jell,

If you followed my instructions and called all you objects as I did, then This:
            1. InputF as text. This field should stay unbound.
                  In the AfterUpdate event of this field, put the following routine:
                  Private Sub InputF_AfterUpdate()
                      Me.Filter = "[MyIndex] = '" & BuildTheIndex(Me.InputF.Value) & "'"
                      Me.FilterOn = True
                  End Sub


Should read:
            1. InputF as text. This field should stay unbound.
                  In the AfterUpdate event of this field, put the following routine:
                  Private Sub InputF_AfterUpdate()
                      Me.Filter = "[TheIndex] = '" & BuildTheIndex(Me.InputF.Value) & "'"   'This line changed!!!
                      Me.FilterOn = True
                  End Sub

HTH,

Nosterdamus
jell,

I must to go off line for the week end....

I'll wait few more minutes for your reply. after that I'll be gone till Monday.

So harry UP.
ASKER CERTIFIED SOLUTION
Avatar of Nosterdamus
Nosterdamus
Flag of Israel image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Great work Nostre

increased points to 300

see u soon

regards

jell
Hi jell,

Found a way to login durring weekend to see how you progress.

Glad I could help & good luck with your app.

;-)

Nosterdamus

p.s. Thanx for the extra points :-)