Sign in to follow this  
Followers 0
Morthawt

Issue with replacing characters between column and row data:

21 posts in this topic

#1 ·  Posted (edited)

I am trying to make a program to generate a 26x26 grid with each row containing the complete alphabet with randomized case where when you look at the columns they are also a complete alphabet that is multi case. All of which must not repeat any letters regardless of the case.

I have tried numerous methods from generating 26 rows and then trying to remake a part of a row based on a detected duplicate letter in a column to what I am doing now which is trying to do it as I am making the row data so the rows get created to where each column is being built from the row data and is made from scratch to not have any duplicate letters etc.

I am no autoit expert but I think my idea of checking the column data for the row the letter I am generating sits on for dupes and replacing the letter if it is detected should work flawlessly. But then again all my other concepts should have worked too. I have added a line to generate a msg box with the row data upon a row completion. The problem is it only gets to a certain line and then just stops.. maybe it will hit line 4 or 7 or something and then the program just is still running and does not generate anything else.

Can someone have a look over my code and see what you think?

Global $row[30]
Global $col[30]
Global $pool = ''
Global $dupesexist = 0
$dedupedone = 0
$undupecol = 0
$pool = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'
$available = 0
$col2fix = 0
Global $deduped = 0
 
GenerateAllRows()
;~ Fakedata()
DedupeAllCols()
 
 
Func Fakedata()
$row[1] = 'wZdkTpoLmFANcuGvbjRHsxeIqY'
$row[2] = 'iDsubeklTQnYMWRpjZoxGvfahc'
$row[3] = 'okAYlmxcQTdngbVRjhPfWziESu'
$row[4] = 'NXqKUwMJTyOpBHVcAIfsLDzger'
$row[5] = 'fNiwytbeHgqDZUOjkpSRVAxcLM'
$row[6] = 'mQBNOZueXiATdSclgfHKRPYVwJ'
$row[7] = 'cYITaNEgxUvqwobRsLjMKFPhzD'
$row[8] = 'ejsVoNIxDAmyhTUqfCzWPBrLGk'
$row[9] = 'DYteUwispZXMcONqrGKVJLabFH'
$row[10] = 'lPRUMfIKtsZjVXcGHdYNOEqWBA'
$row[11] = 'yAGPXesBDTLvFjNrMKQzCIHUoW'
$row[12] = 'JvRyBqZnOFxEhPusmcAlTwgdki'
$row[13] = 'boRwyZUJntakPXsgcLVeIHDqMF'
$row[14] = 'ZhPUiBXwgKjmSVfcOELYArntDq'
$row[15] = 'pXESdnuQrkcilGwHZABofmVJYT'
$row[16] = 'xHeriFtuvmqwlSPkybdJzgcoaN'
$row[17] = 'UWPVGMryOkfzEDQaIsnchjBxTL'
$row[18] = 'SicmoBzdWJRqHnALfeUGyktpxv'
$row[19] = 'kYxeFAMICLDHsjQRWBgTnUoZvP'
$row[20] = 'VrfCKoWHIaZgNDbUjTxQmyLSPE'
$row[21] = 'aKuwrotlEXDhszmYPVCibQJFnG'
$row[22] = 'gLYnzmrxcQBjAVsHfdwPetukio'
$row[23] = 'qgadUOhFPiNmtkLJVYebXSwrcZ'
$row[24] = 'hLyzTSVoQGxpfwEAIJmDUcKnRb'
$row[25] = 'tQKrwCVayGphenLFbZIUdOSmjX'
$row[26] = 'RwCZBOVFehgdXKJipLtAQnMYuS'
WriteRows('original')
 
EndFunc   ;==>Fakedata
 
 
 
Func DedupeAllCols()
Global $deduped = 0
 
While $deduped <> 26
  $deduped = $deduped + 1
;~  MsgBox(0, 0, 'De-duping now')
  DupeFixer($deduped)
;~  Sleep(100)
;~  WriteRows('result')
;~  MsgBox(0, 0, 'Done with row ' & $deduped)
  If $deduped = 26 Then ExitLoop
WEnd
 
WriteRows('result')
EndFunc   ;==>DedupeAllCols
 
Func GenerateAllRows()
For $currentrow = 1 To 26
  GenerateRow($currentrow)
Next
WriteRows('original')
GenerateCols()
EndFunc   ;==>GenerateAllRows
 
Func GenerateRow($rowgen)
$count = 0
$available = $pool
$row[$rowgen] = ''
$dupedetected = 0
 
If $row[1] = '' Then
  For $count = 1 To 26 Step 1
   $availablelength = StringLen($available)
   $char = Random(1, $availablelength, 1)
   $actualchar = StringMid($available, $char, 1)
   $row[$rowgen] = $row[$rowgen] & StringMid($available, $char, 1)
   $available = StringReplace($available, $actualchar, '')
  Next
 
Else
 
  For $count = 1 To 26 Step 1
   GenerateCols()
   $availablelength = StringLen($available)
   $char = Random(1, $availablelength, 1)
   $actualchar = StringMid($available, $char, 1)
   If StringInStr($col[$count], $actualchar) <> 0 Then
    While 1
     $char = Random(1, $availablelength, 1)
     $actualchar = StringMid($available, $char, 1)
     If StringInStr($col[$count], $actualchar) = 0 Then ExitLoop
    WEnd
 
   EndIf
 
   $row[$rowgen] = $row[$rowgen] & StringMid($available, $char, 1)
   $available = StringReplace($available, $actualchar, '')
   If StringLen($row[$rowgen]) = 26 Then MsgBox(0,$rowgen ,$row[$rowgen])
 
  Next
 
EndIf
 
 
EndFunc   ;==>GenerateRow
 
Func WriteRows($type)
If $type = 'original' Then
  FileDelete(@DesktopDir & '\original.txt')
  $writecount = 0
  For $writecount = 1 To 26
   FileWrite(@DesktopDir & '\original.txt', $row[$writecount] & @CRLF)
  Next
Else
  FileDelete(@DesktopDir & '\result.txt')
  $writecount = 0
  For $writecount = 1 To 26
   FileWrite(@DesktopDir & '\result.txt', $row[$writecount] & @CRLF)
  Next
EndIf
 
EndFunc   ;==>WriteRows
 
Func GenerateCols()
$c = 1
$r = 1
 
For $pre = 1 To 26 Step 1
  $col[$pre] = ''
Next
 
While $c <> 27
  For $r = 1 To 26 Step 1
   $col[$c] = $col[$c] & StringMid($row[$r], $c, 1)
  Next
  $c = $c + 1
  If $c = 27 Then ExitLoop
 
WEnd
EndFunc   ;==>GenerateCols
 
Func Dupechecker($checkcolfordupes)
GenerateCols()
$isthisadupe = 0
For $count = 1 To 26
  $chartocheck = StringMid($col[$checkcolfordupes], $count, 1)
  $isthisadupe = $isthisadupe + StringInStr($col[$checkcolfordupes], $chartocheck, 0, 2)
  If $isthisadupe > 0 Then $dupesexist = 1
 
Next
 
EndFunc   ;==>Dupechecker
 
 
 
Func DupeFixer($undupecol)
$isthisadupe = 0
$dupesexist = 0
GenerateCols()
For $count = 1 To 26
 
  $chartocheck = StringMid($col[$undupecol], $count, 1)
  $isthisadupe = StringInStr($col[$undupecol], $chartocheck, 0, 2)
;~   MsgBox(0, 'Length:  ' & StringLen($col[$undupecol]), $isthisadupe)
  If $isthisadupe > 0 Then
   $dupecase = StringMid($col[$undupecol], $isthisadupe, 1)
   $row[$isthisadupe] = StringReplace($row[$isthisadupe], $chartocheck, '') & $dupecase
   GenerateCols()
;~  $count = $count - 1
  EndIf
 
Next
 
EndFunc   ;==>DupeFixer

Also if you know of a better more elegant method of generating this kind of grid please feel free to share how. All columns and rows must contain every letter of the alphabet and be random case letters, no duped letters.

Edited by Morthawt

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

I'm sure there's a better way, but here's what I came up with:

#include <array.au3>

;ASCII Capital letters: 65-90; lowercase letters: 97-122

Dim $used[26]

$i = 0
Do
$var = Random(65, 90, 1) ; Random capital letters only
_ArraySearch($used, $var) ; Only unique numbers
    If @error Then
        $used[$i] = $var
        $i += 1
    EndIf
Until $i = 26

Dim $charArray[26] ; Convert numbers to characters
For $i = 0 to 25 Step 1
    $capLow = Random(0, 1, 1) ; This section selects randomly between caps and lowercase
    If $capLow = 0 Then
        $charArray[$i] = Chr($used[$i])
    Else
        $charArray[$i] = Chr($used[$i] + 32)
    EndIf
Next

_ArrayDisplay($charArray) ; Displays output as an array
$string = _ArrayToString($charArray, "")
MsgBox(0, "", $string) ; Displays output as a string
Edited by sleepydvdr

#include <ByteMe.au3>

Share this post


Link to post
Share on other sites

I am confused how I would use that though. What I need to do is generate the first row of random case alphabet (which is easy) and then starting from the second line I need to check the character generated with all the characters above it to make sure it does not appear already and if it does appear then to select a different letter so that duplicate letter can be used further in the row where it will not have a dupe problem with any other letters above in a column. Everything I try keeps failing. The program will just sit there forever. Here is what I am working on right now:

Global $row[28]
Global $col[28]
$pool = 'QWERTYUIOP'
$available = $pool
$size = StringLen($pool)
$size1 = $size + 1
$r = 1
While $r <> $size1
$available = $pool
For $c = 1 To $size Step 1
  $length = StringLen($available)
  $charpos = Random(1, $length, 1)
  $actualchar = StringMid($available, $charpos, 1)
  $dupe = StringInStr($col[$c], $actualchar)
  If $r <> $size Then
   If StringRegExp($col[$c], '(?i)[' & $actualchar & ']') = 1 Then
    While 1
;~    MsgBox(0,'',StringRegExp($col[$c], '(?i)[' & $actualchar & ']'))
;~    MsgBox(0,0,$actualchar)
;~    $length = StringLen($available)
     SRandom(Random())
     $charpos = Random(1, $length, 1)
     $actualchar = StringMid($available, $charpos, 1)
     MsgBox(0,$r,$col[$c] & '   ' & $actualchar & '-- ' & $available)
     If StringRegExp($col[$c], '(?i)[' & $actualchar & ']') = 0 Then ExitLoop
    WEnd

   EndIf
  EndIf

  If $length = 1 Then $actualchar = StringMid($available, 1, 1)
  $row[$r] = $row[$r] & $actualchar
  $col[$c] = $col[$c] & $actualchar
  $available = StringReplace($available, $actualchar, '')

Next
$r = $r + 1
WEnd
FileDelete(@DesktopDir & '\result.txt')
For $w = 1 To 6 Step 1
FileWrite(@DesktopDir & '\result.txt', $row[$w] & @CRLF)
Next
;~ MsgBox(0, 0, $col[6])

I have no idea what is going on.

Share this post


Link to post
Share on other sites

So basically, you are making a crossword puzzle kind of thing and you want every row and every column to be random. If that's what you are trying to do, I think you will run in to a problem with repetition. You can do a completely random horizontal row and random vertical column, but then you will be limited in what fills the rest of the space. I think it would have to be a diagonal repetitive pattern to fulfill your needs.

If you told me what this is for, it might help me understand.


#include <ByteMe.au3>

Share this post


Link to post
Share on other sites

I am trying to make a Latin square I think they are called, as seen here: https://www.grc.com/OffTheGrid.htm

It is used to make secure passwords, the requirement being that when you generate the characters that the rows and columns all contain the whole alphabet and there are no repetitive letters. It is not that complicated and I do not know why I am having issues. I have come up with at least 2 different ways that should work. Typically they work up to a few small columns and then go into an infinite loop or something.

Share this post


Link to post
Share on other sites

This is not perfect, but the best I can come up with so far. Maybe it will help you with some ideas:

#include <array.au3>
 
;ASCII Capital letters: 65-90; lowercase letters: 97-122
 
Dim $tempArray[26]
Dim $array[26][26]
 
For $h = 0 to 25 step 1
    _GenerateRandom()
    For $i = 0 To 25 Step 1
        $array[$h][$i] = $tempArray[$i]
    Next
Next
 
_UniqueCheck()
 
_ArrayDisplay($array)
 
Func _GenerateRandom()
    For $j = 0 to 25 Step 1
        $rand = Random(65, 90, 1)
        $tempArray[$j] = $rand
        _ArraySearch($tempArray, $rand) ; Only unique numbers
        If @error Then
            $tempArray[$i] = $rand
            $j += 1
        EndIf
    Next
 
    For $i = 0 to 25 Step 1
    $capLow = Random(0, 1, 1) ; This section selects randomly between caps and lowercase
    If $capLow = 0 Then
        $tempArray[$i] = Chr($tempArray[$i])
    Else
        $tempArray[$i] = Chr($tempArray[$i] + 32)
    EndIf
 
Next
EndFunc
 
Func _UniqueCheck()
If _ArrayUnique($array, 0) <> 0 Then
    For $h = 0 to 25 step 1
        _GenerateRandom()
        For $i = 0 To 25 Step 1
            $array[$h][$i] = $tempArray[$i]
        Next
    Next
EndIf
 
If _ArrayUnique($array, 1) <> 0 Then
    For $h = 0 to 25 step 1
        _GenerateRandom()
        For $i = 0 To 25 Step 1
            $array[$h][$i] = $tempArray[$i]
        Next
    Next
EndIf
EndFunc

#include <ByteMe.au3>

Share this post


Link to post
Share on other sites

Here is a method of generating unique characters in each row and each column of an array.

#include <array.au3>

Local $str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
Local $aCharArray = StringSplit($str, "", 2)
Local $iSize = UBound($aCharArray)
;_ArrayDisplay($aCharArray)

Local $aArray[$iSize][$iSize]
Local $sRow, $index = "|", $Rand

; Generate one random line of all characters. (The string to be rotated.)
For $r = 0 To $iSize - 1
    Do
        $Rand = $aCharArray[Random(0, ($iSize - 1), 1)]
    Until (StringInStr($sRow, $Rand, 1) = 0)
    $sRow &= $Rand
Next
$Rand = ""

For $r = 0 To $iSize - 1
    Do
        $Rand = Random(0, ($iSize - 1), 1)
    Until (StringInStr($index, "|" & $Rand & "|", 1) = 0)
    $index &= $Rand & "|"
    $sRow = StringTrimRight(StringRight($sRow, 1) & $sRow, 1) ; Rotate string by one character (last character moved to be first character).
    Local $temp = StringSplit($sRow, "", 2)
    For $c = 0 To UBound($temp) - 1
        $aArray[$Rand][$c] = $temp[$c] ; Place the split rotated string randomly into one of the rows of an array.
    Next
Next
;ConsoleWrite($index & @LF)
_ArrayDisplay($aArray)

Share this post


Link to post
Share on other sites

Here is a method of generating unique characters in each row and each column of an array.

#include <array.au3>
 
Local $str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
Local $aCharArray = StringSplit($str, "", 2)
Local $iSize = UBound($aCharArray)
;_ArrayDisplay($aCharArray)
 
Local $aArray[$iSize][$iSize]
Local $sRow, $index = "|", $Rand
 
; Generate one random line of all characters. (The string to be rotated.)
For $r = 0 To $iSize - 1
    Do
        $Rand = $aCharArray[Random(0, ($iSize - 1), 1)]
    Until (StringInStr($sRow, $Rand, 1) = 0)
    $sRow &= $Rand
Next
$Rand = ""
 
For $r = 0 To $iSize - 1
    Do
        $Rand = Random(0, ($iSize - 1), 1)
    Until (StringInStr($index, "|" & $Rand & "|", 1) = 0)
    $index &= $Rand & "|"
    $sRow = StringTrimRight(StringRight($sRow, 1) & $sRow, 1) ; Rotate string by one character (last character moved to be first character).
    Local $temp = StringSplit($sRow, "", 2)
    For $c = 0 To UBound($temp) - 1
        $aArray[$Rand][$c] = $temp[$c] ; Place the split rotated string randomly into one of the rows of an array.
    Next
Next
;ConsoleWrite($index & @LF)
_ArrayDisplay($aArray)

Steve Gibson talked about this on the Securitynow pod cast and said while it will work, the level of entropy is not enough to have the maximum possible combinations. The best way is completely random with no "shifting" to get the unique result.

Share this post


Link to post
Share on other sites

This code is completely rewritten and is as basic as I can make it. It works for a few rows and then just locks up and does no more. The script keeps running but does nothing:

$pool = 'QWERTYUIOPASDFGHJKLZXCVBNM'
$available = $pool
Global $row[28]
Global $col[28]
Global $a
Global $length
Global $char
Global $available
FileDelete(@DesktopDir & '\result.txt')
$r = 1
For $a = 1 To 26 Step 1
$length = StringLen($available)
$random = Random(1, $length, 1)
$char = StringMid($available, $random, 1)
$row[$r] = $row[$r] & $char
$col[$a] = $col[$a] & $char
$available = StringReplace($available, $char, '')
Next
FileWrite(@DesktopDir & '\result.txt', $row[$r] & @CRLF)
$r = $r + 1

While 1
$available = $pool
For $a = 1 To 26 Step 1
  $length = StringLen($available)
  While 1
   $random = Random(1, $length, 1)
   $char = StringMid($available, $random, 1)
   If StringInStr($col[$a], $char) = 0 Then ExitLoop
  WEnd
  $row[$r] = $row[$r] & $char
  $col[$a] = $col[$a] & $char
  $available = StringReplace($available, $char, '')
Next
If $r = 26 Then ExitLoop
FileWrite(@DesktopDir & '\result.txt', $row[$r] & @CRLF)
$r = $r + 1
WEnd

As I said, it is working and doing what it is supposed to do, only it does not keep doing it past 7 - 12 rows or so. Any ideas?

Share this post


Link to post
Share on other sites

So far I have it working but it is only getting to about row 19 before getting VERY slow indeed. Can you test this out (I added console writes so you can see the rows and it also saves to a file when each row is completed) and see if you can suggest some way to speed the process up? Thanks

Global $data[27][27]
Global $r
Global $pool
Global $available
Global $colstring
Global $rowstring
Global $skipped
FileDelete(@DesktopDir & '\result.txt')
$pool = 'QWERTYUIOPASDFGHJKLZXCVBNM'

GenerateAllRows()

Func GenerateAllRows()
For $g = 1 To 26 Step 1
  GenerateRow($g)
  BuildRow($g)
  $count = $g
  If StringLen($g) = 1 Then $count = 0 & $g
  If $rowstring <> '' Then
   ConsoleWrite('Row: ' & $count & ' - ' & $rowstring & @CRLF)
   FileWrite(@DesktopDir & '\result.txt', $rowstring & @CRLF)
  Else
   While $rowstring = ''
    SRandom(Random(1, 9999999999, 1))
    GenerateRow($g)
    BuildRow($g)
   WEnd
   FileWrite(@DesktopDir & '\result.txt', $rowstring & @CRLF)
   ConsoleWrite('Row: ' & $count & ' - ' & $rowstring & @CRLF)
  EndIf
;~  If $rowstring = '' Then ConsoleWrite('Skipped row: ' & $g & @CRLF)
Next
EndFunc   ;==>GenerateAllRows

Func GenerateRow($row)
$available = $pool
$try = 0

For $a = 1 To 26 Step 1
  $length = StringLen($available)
  $random = Random(1, $length, 1)
  $char = StringMid($available, $random, 1)
  If $length = 1 Then $char = $available
  BuildCol($a)
  While StringInStr($colstring, $char) <> 0
   $random = Random(1, $length, 1)
   $char = StringMid($available, $random, 1)
   If $length = 1 Then $char = $available
   $try = $try + 1
   If $try > 27 Then
    ResetRow($row)
    ExitLoop (2)

   EndIf
  WEnd
  $data[$row][$a] = $char
  $available = StringReplace($available, $char, '')

Next
EndFunc   ;==>GenerateRow

Func BuildCol($col)
$colstring = ''
For $a = 1 To 26 Step 1
  $colstring = $colstring & $data[$a][$col]
Next
EndFunc   ;==>BuildCol

Func BuildRow($row)
$rowstring = ''
For $a = 1 To 26 Step 1
  $rowstring = $rowstring & $data[$row][$a]
Next
EndFunc   ;==>BuildRow
 

Func ResetRow($row)
For $a = 1 To 26 Step 1
  $data[$row][$a] = ''
Next
EndFunc   ;==>ResetRow

Func ListSkippedLine($skipcheck)
BuildRow($skipcheck)
If $rowstring = '' Then
  $skipped = $skipcheck
EndIf
EndFunc   ;==>ListSkippedLine

Share this post


Link to post
Share on other sites

I changed the the line If $try > 27 Then to If $try > 1000 Then and I was able to complete the grid in about 20 seconds on my laptop. I think the reason it was taking so long before is because you're resetting the row data so often that it has less chance of completing in a timely fashion.


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

With the default of 27 as per your script, I completed 21 rows in about 15 minutes compared to all 26 in 30 seconds just by changing that one variable.


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

Yes, the only problem is it is supposed to take only 2 or 3 seconds. Check this page out and hit the "get it done" button https://www.grc.com/offthegrid.htm

I need to generate it very quickly. The thing is if my letters that are left cannot be used because they all collide with the column, then I scratch that attempt and generate a whole new random line to replace it. That way I am starting that row from scratch if its "impossible" to solve. The issue is I cannot seem to get it to do this very very quickly.

This source code shows you the data that is being generated to try and make a row. You will see it swap and change once it fails. I am trying to speed the process up but running into brick walls. I know its possible, I just am not sure how I am going to do this.

Global $data[27][27]
Global $r
Global $pool
Global $available
Global $colstring
Global $rowstring
Global $skipped
Global $backupavailable
FileDelete(@DesktopDir & '\result.txt')
$pool = 'QWERTYUIOPASDFGHJKLZXCVBNM'

GenerateAllRows()

Func GenerateAllRows()
For $g = 1 To 26 Step 1
  GenerateRow($g)
  BuildRow($g)
  $count = $g
  If StringLen($g) = 1 Then $count = 0 & $g
  If $rowstring <> '' Then
   ConsoleWrite('Row: ' & $count & ' - ' & $rowstring & @CRLF)
   FileWrite(@DesktopDir & '\result.txt', $rowstring & @CRLF)
  Else
   While $rowstring = ''
    SRandom(Random(1, 9999999999, 1))
    GenerateRow($g)
    BuildRow($g)
   WEnd
   FileWrite(@DesktopDir & '\result.txt', $rowstring & @CRLF)
   ConsoleWrite('Row: ' & $count & ' - ' & $rowstring & @CRLF)
  EndIf
;~  If $rowstring = '' Then ConsoleWrite('Skipped row: ' & $g & @CRLF)
Next
EndFunc   ;==>GenerateAllRows

Func GenerateRow($row)
$available = $pool
$try = 0
$currentrow = ''
$backupavailable = ''
For $a = 1 To 26 Step 1
  $length = StringLen($available)
  $random = Random(1, $length, 1)
  $char = StringMid($available, $random, 1)
  If $length = 1 Then $char = $available
  BuildCol($a)
  While StringInStr($colstring, $char) <> 0
   $backupavailable = $available
   $length = StringLen($available)
   $random = Random(1, $length, 1)
   $char = StringMid($available, $random, 1)
   If $length = 1 Then $char = $available
;~    If StringInStr($colstring, $char) <> 0 Then $available = StringReplace($available, $char, '')
   $try = $try + 1
   If $try > 1000 Then
    ResetRow($row)
    ExitLoop (2)

   EndIf
  WEnd
;~   $available = $backupavailable
  $data[$row][$a] = $char
  $currentrow = $currentrow & $char
  $available = StringReplace($available, $char, '')
  ConsoleWrite('Row ' & $row & ' Data so far: ' & $currentrow & @CRLF)
 
Next
EndFunc   ;==>GenerateRow

Func BuildCol($col)
$colstring = ''
For $a = 1 To 26 Step 1
  $colstring = $colstring & $data[$a][$col]
Next
EndFunc   ;==>BuildCol

Func BuildRow($row)
$rowstring = ''
For $a = 1 To 26 Step 1
  $rowstring = $rowstring & $data[$row][$a]
Next
EndFunc   ;==>BuildRow
 

Func ResetRow($row)
For $a = 1 To 26 Step 1
  $data[$row][$a] = ''
Next
EndFunc   ;==>ResetRow

Func ListSkippedLine($skipcheck)
BuildRow($skipcheck)
If $rowstring = '' Then
  $skipped = $skipcheck
EndIf
EndFunc   ;==>ListSkippedLine

I have gone from thinking its impossible, to generating ideas on how do to it. Disproving those ideas (eventually through debugging the code manually using console writes) and now I have it working and just need to improve the speed, to make it optimal. I am progressing, this seems to be my final battle right now. I am 90% sure the data in the rows is not duping when looked at as a column, so speed is the issue now.

Share this post


Link to post
Share on other sites

Upon using the code I pasted in my last message, I can see that when it ditches its attempt to solve the row and restart the row from scratch that it sometimes is using the same letters and thus partially making the same selections as it did on the previous attempts. If I can find a way to prevent it from trying the same combinations it would speed things up to being almost instant as I am looking for.. The question is how... Hmm

Share this post


Link to post
Share on other sites

#17 ·  Posted (edited)

I have made something that appears to still work ok and has a save as dialogue etc. I still cannot increase the speed though...

#include <ButtonConstants.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include <File.au3>
Global $data[27][27]
Global $r
Global $Form1_1
Global $pool
Global $available
Global $colstring
Global $rowstring
Global $skipped
Global $backupavailable
Global $number
Global $indicator
Global $percent
Local $drive
Global $dupecount
Local $dir
Local $filename
Global $file
Local $ext
FileDelete($file)
$pool = 'QWERTYUIOPASDFGHJKLZXCVBNM'
$file = FileSaveDialog('Save Latin Square', @DesktopDir, 'Text files (*.txt)', 16)
_PathSplit($file, $drive, $dir, $filename, $ext)
If $ext = '' Then $file = $file & '.txt'
If FileExists($file) Then FileDelete($file)
GenerateAllRows()
;~ CheckColForDupes(21)
MsgBox(0,'Completed', 'Grid has been completed')
 
 
Func GenerateAllRows()
$number = 0
For $g = 1 To 26 Step 1
  GenerateRow($g)
  $number = $number + 1
  BuildRow($g)
  $count = $g
  If StringLen($g) = 1 Then $count = 0 & $g
  If $rowstring <> '' Then
   ConsoleWrite('Row: ' & $count & ' - ' & $rowstring & @CRLF)
   FileWrite($file, $rowstring & @CRLF)
   $percent = ($g / 26) * 100
 
  Else
   While $rowstring = ''
;~   SRandom(Random(1, 9999999999, 1))
    GenerateRow($g)
    $number = $number + 1
    BuildRow($g)
   WEnd
   FileWrite($file, $rowstring & @CRLF)
   ConsoleWrite('Row: ' & $count & ' - ' & $rowstring & @CRLF)
  EndIf
 
Next
EndFunc   ;==>GenerateAllRows
 
Func GenerateRow($row)
$available = $pool
$try = 0
$currentrow = ''
$backupavailable = ''
For $a = 1 To 26 Step 1
  $length = StringLen($available)
  $random = Random(1, $length, 1)
  $char = StringMid($available, $random, 1)
  If $length = 1 Then $char = $available
  BuildCol($a)
  While StringInStr($colstring, $char) <> 0
   $backupavailable = $available
   $length = StringLen($available)
   $random = Random(1, $length, 1)
   $char = StringMid($available, $random, 1)
   If $length = 1 Then $char = $available
   $try = $try + 1
   If $try > 1000 Then
    ResetRow($row)
    ExitLoop (2)
 
   EndIf
  WEnd
 
  $data[$row][$a] = $char
  $currentrow = $currentrow & $char
  $available = StringReplace($available, $char, '')
  $percent = Round(($row / 26) * 100, 0)
;~   ConsoleWrite('Row ' & $row & ' Data so far: ' & $currentrow & @CRLF)
;~   ConsoleWrite('Number of generated rows: ' & $number & @CRLF)
  If StringLen($currentrow) = 26 Then GUICtrlSetData($indicator, Round($percent, 0) & ' percent complete')
 
Next
EndFunc   ;==>GenerateRow
 
Func BuildCol($col)
$colstring = ''
For $a = 1 To 26 Step 1
  $colstring = $colstring & $data[$a][$col]
Next
EndFunc   ;==>BuildCol
 
Func BuildRow($row)
$rowstring = ''
For $a = 1 To 26 Step 1
  $rowstring = $rowstring & $data[$row][$a]
Next
EndFunc   ;==>BuildRow
 
 
Func CheckColForDupes($dupe)
BuildCol($dupe)
$dupecount = 0
For $d = 1 To 26 Step 1
$char = StringMid($colstring, $d, 1)
If StringInStr($colstring, $char, 0, 2) <> 0 Then $dupecount = $dupecount + 1
Next
 
EndFunc   ;==>CheckColForDupes
 
 
 
Func ResetRow($row)
For $a = 1 To 26 Step 1
  $data[$row][$a] = ''
Next
EndFunc   ;==>ResetRow
 
Func ListSkippedLine($skipcheck)
BuildRow($skipcheck)
If $rowstring = '' Then
  $skipped = $skipcheck
EndIf
EndFunc   ;==>ListSkippedLine

Any thoughts?

Edited by Morthawt

Share this post


Link to post
Share on other sites

I've been working at this for the last day now to see if I could make it any faster, unfortunately the speed of an interpreted language just isn't going to be very fast for something like this.

Global $Timer = TimerInit()
Global $Array[26][26], $ColumnData[26][26]
Global $Pool = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Global $FileName = @ScriptDir & "\LatinSquare.txt"
 
_GenerateAllRows()
 
Func _GenerateAllRows()
    Local $File = FileOpen($FileName, 2)
    For $iRow = 0 To 25
        While Not _GenerateRow($iRow) ; If this returns false, then a row wasn't generated
             _Reset($iRow) ; clears the current row and column data, also reseed the RNG
 
            ;the following code chooses a random pattern for $Pool from the previously used patterns
            ;this helps generate new random patterns in combination with the reseed operation above
            ;so that hopefully it will finish faster
            $Pool = ""
            $Ran = Random(0, $iRow - 1, 1)
            For $Loop = 0 To 25
                $Pool &= $Array[$Ran][$Loop]
            Next
 
        WEnd
        ConsoleWrite("Row # " & $iRow & @CRLF)
        ;the following writes the current line to a text file, the characters are delimited by the "|" character
        $Line = ""
        For $X = 0 To 25
            $Line &= $Array[$iRow][$X] & "|"
        Next
        $Line = StringTrimRight($Line, 1)
        FileWriteLine($File, $Line)
    Next
    $Time = Round(TimerDiff($Timer) / 1000, 2)
    FileWriteLine($File, "Run time: " & $Time) ; writes the execution time to the text file
    FileClose($File)
    FileCopy($FileName, @ScriptDir & "\LatinSquare " & $Time & ".txt")
EndFunc ;==>_GenerateAllRows
 
Func _GenerateRow($RowNum) ;Generate a row
    Local $Loop = 0, $Length, $Random, $Char, $Available = $Pool
    For $I = 0 To 25
        $Length = StringLen($Available) ; as letters are placed, the length gets shorter by one
        If $Length > 1 Then
            $Random = Random(1, $Length, 1)
            $Char = StringMid($Available, $Random, 1)
        Else ; if $Available is down to the last character, then set $char to that character rather than trying to use Random
            $Char = $Available
        EndIf
        While _CheckCol($Char, $I) ; checks to make sure that character hasn't been used in that column already, if it has,  run the following code.
            $Random = Random(1, $Length, 1)
            $Char = StringMid($Available, $Random, 1)
            $Loop += 1
            If $Loop > 100 Then Return False
        WEnd
        $Loop = 0
        $Array[$RowNum][$I] = $Char
        $ColumnData[$RowNum][$I] = $Char
        $Available = StringReplace($Available, $Char, '')
    Next
    Return True
EndFunc ;==>_GenerateRow
 
Func _CheckCol($Letter, $Column)
    For $I = 0 To 25
        If $ColumnData[$I][$Column] = $Letter Then
            Return SetError(4, 0, True)
        EndIf
    Next
    Return False
EndFunc ;==>_CheckCol
 
Func _Reset($Row)
    $Seed = Random(1, 2147483647, 1)
    SRandom($Seed)
    For $I = 0 To 25
        $Array[$Row][$I] = ""
        $ColumnData[$Row][$I] = ""
    Next
EndFunc ;==>_Reset

The problem with this code is that it will sometimes take as little as 2 seconds and sometimes takes over 2 minutes.

It creates a text file called LatinSquare.txt, and then copies it to a new file called LatinSquare<run time>.txt so that if it takes 3.44 seconds to run, the copied file is named "LatinSquare 3.44.txt". I've tried to make the randomness a little bit better by reseeding the RNG and then choosing a previous row's characters for the $Pool variable so that the character layout isn't the same one every time.


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

I've been working at this for the last day now to see if I could make it any faster, unfortunately the speed of an interpreted language just isn't going to be very fast for something like this.

Global $Timer = TimerInit()
Global $Array[26][26], $ColumnData[26][26]
Global $Pool = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Global $FileName = @ScriptDir & "\LatinSquare.txt"
 
_GenerateAllRows()
 
Func _GenerateAllRows()
    Local $File = FileOpen($FileName, 2)
    For $iRow = 0 To 25
        While Not _GenerateRow($iRow) ; If this returns false, then a row wasn't generated
             _Reset($iRow) ; clears the current row and column data, also reseed the RNG
 
            ;the following code chooses a random pattern for $Pool from the previously used patterns
            ;this helps generate new random patterns in combination with the reseed operation above
            ;so that hopefully it will finish faster
            $Pool = ""
            $Ran = Random(0, $iRow - 1, 1)
            For $Loop = 0 To 25
                $Pool &= $Array[$Ran][$Loop]
            Next
 
        WEnd
        ConsoleWrite("Row # " & $iRow & @CRLF)
        ;the following writes the current line to a text file, the characters are delimited by the "|" character
        $Line = ""
        For $X = 0 To 25
            $Line &= $Array[$iRow][$X] & "|"
        Next
        $Line = StringTrimRight($Line, 1)
        FileWriteLine($File, $Line)
    Next
    $Time = Round(TimerDiff($Timer) / 1000, 2)
    FileWriteLine($File, "Run time: " & $Time) ; writes the execution time to the text file
    FileClose($File)
    FileCopy($FileName, @ScriptDir & "\LatinSquare " & $Time & ".txt")
EndFunc ;==>_GenerateAllRows
 
Func _GenerateRow($RowNum) ;Generate a row
    Local $Loop = 0, $Length, $Random, $Char, $Available = $Pool
    For $I = 0 To 25
        $Length = StringLen($Available) ; as letters are placed, the length gets shorter by one
        If $Length > 1 Then
            $Random = Random(1, $Length, 1)
            $Char = StringMid($Available, $Random, 1)
        Else ; if $Available is down to the last character, then set $char to that character rather than trying to use Random
            $Char = $Available
        EndIf
        While _CheckCol($Char, $I) ; checks to make sure that character hasn't been used in that column already, if it has,  run the following code.
            $Random = Random(1, $Length, 1)
            $Char = StringMid($Available, $Random, 1)
            $Loop += 1
            If $Loop > 100 Then Return False
        WEnd
        $Loop = 0
        $Array[$RowNum][$I] = $Char
        $ColumnData[$RowNum][$I] = $Char
        $Available = StringReplace($Available, $Char, '')
    Next
    Return True
EndFunc ;==>_GenerateRow
 
Func _CheckCol($Letter, $Column)
    For $I = 0 To 25
        If $ColumnData[$I][$Column] = $Letter Then
            Return SetError(4, 0, True)
        EndIf
    Next
    Return False
EndFunc ;==>_CheckCol
 
Func _Reset($Row)
    $Seed = Random(1, 2147483647, 1)
    SRandom($Seed)
    For $I = 0 To 25
        $Array[$Row][$I] = ""
        $ColumnData[$Row][$I] = ""
    Next
EndFunc ;==>_Reset

The problem with this code is that it will sometimes take as little as 2 seconds and sometimes takes over 2 minutes.

It creates a text file called LatinSquare.txt, and then copies it to a new file called LatinSquare<run time>.txt so that if it takes 3.44 seconds to run, the copied file is named "LatinSquare 3.44.txt". I've tried to make the randomness a little bit better by reseeding the RNG and then choosing a previous row's characters for the $Pool variable so that the character layout isn't the same one every time.

I tried reseeding too (not sure if I left it commented out or took it out though). I do not understand the varying delays in completion time. Whether it is your code or mine, sometimes it is fast and others it is slow. Steve Gibson's one uses javascript, so I am not sure how that is so different from Autoit script in terms of execution times etc.

Share this post


Link to post
Share on other sites

Have you tried his site and used the radio button for "See how it works" or however it is titled? It can take a VERY long time to create the grid when you're watching it work, which leads me to believe that it might be creating random grids behind the scenes and only displaying the completed ones.

I have elaborated on this idea to create a fully functioning password creator using Latin Squares. I have noticed a flaw in using them though, if there are repeated letters in the input string, the result is a little confusing. Although if you follow the logic of the script (which I will post later when I'm at the computer it's stored on), you can see how it is getting the output that it generates for the password. At least it's consistently getting the same password every time, so the logic isn't at fault, just my implementation of it. :mellow:


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0