Jump to content
Sign in to follow this  
BitByteBit

Send More Money

Recommended Posts

BitByteBit

Somebody set me a puzzle. I was to work out the values of each letter in this equation: "Send + More = Money".

I sat down with a pen and paper and worked it out, but then I wanted to write a script that could work it out through brute force and or guessing.

This is what I came up with:

#cs
     SEND
    +MORE
    MONEY
#ce
Global $Array[2][10] = [["0", "1", "X", "X", "X", "X", "X", "X", "X", "X"],[0]]
$Array[1][0] = 0
Global $T1, $Send, $More, $Money, $R, $Last
$Solved = False
$C = 1
$T2 = TimerInit()
Do
    $E = _RandomizeNumbers()
    $Send = _RandomizeNumbers() & $E & _RandomizeNumbers() & _RandomizeNumbers()
    $More = $Array[0][1] & $Array[0][0] & _RandomizeNumbers() & $E
    If $Send + $More = StringLeft($More, 2) & StringMid($Send, 3, 1) & $E & 2 Then ;Dirty, dirty cheater...
        $Solved = True
        ExitLoop
    EndIf
    $C += 1
    Global $Array[2][10] = [["0", "1", "X", "X", "X", "X", "X", "X", "X", "X"],[0]]
Until $Solved = True
$T2 = TimerDiff($T2)
ConsoleWrite(@CRLF & "Time taken: " & Round($T2 / 1000, 2) & @CRLF & "Calculations:" & $C & @CRLF & "Send: " & $Send & @CRLF & "More: " & $More & @CRLF & "Money: " & $Send + $More & @CRLF)
Exit

Func _RandomizeNumbers() ;Purpose, to generate a unique random number.
    $R = _Random()
    If $Array[0][$R] = "X" Then
        $Array[0][$R] = $R
    Else
        _RandomizeNumbers()
    EndIf
    Return $R
EndFunc   ;==>_RandomizeNumbers

Func _Random()
    Return Random(0, 9, 1)
EndFunc   ;==>_Random


#cs Lots of boring, useless code...

    $T1 = Random(1, 9, 1) ;E
    $Send = Random(1, 9, 1) & $T1 & Random(1, 9, 1) & Random(1, 9, 1)
    $More = 1 & Random(0, 9, 1) & Random(1, 9, 1) & $T1

    $Y = StringMid($Send,4,1) + StringMid($More,4,1)
    ;If $Send + $More = StringLeft($More,2) & StringMid($Send,3,1) & Random(1, 9, 1)& $T1 & StringMid($Send,4,1) + StringMid($More,4,1)

    ;$C += 1
    $D += 1
    If TimerDiff($T) < 5000 Then
    If TimerDiff($T) > 1000 Then
    ConsoleWrite("CPS: " & $D & @CRLF)
    ;ConsoleWrite("TCE: " & $C & @CRLF)
    $D = 0
    ;$T = TimerInit()
    EndIf
    EndIf

    Func _RandomizeNumbers()
    Global $T1 = Random(1, 9, 1) ;E
    Global $Send = Random(1, 9, 1) & $T1 & Random(1, 9, 1) & Random(1, 9, 1)
    Global $More = Random(1, 9, 1) & Random(1, 9, 1) & Random(1, 9, 1) & $T1
    Global $Money = $Send + $More
    EndFunc   ;==>_RandomizeNumbers

    Func _RandomizeNumbers()
    If $Array[1][0] = 9 Then Global $Array [2][10] =[[0,0], [1,2,0,0,0,0,0]]
    $R = Random(0,9,1)
    If not $Array[0][$R] = $R Then
    $Array[0][$R] = $R
    $Array[1][0] +=1
    Return $R
    Else
    _RandomizeNumbers()
    EndIf
    EndFunc

    Func _RandomizeNumbers()
    If $Array[1][0] = 10 Then Global $Array[2][10]
    ;_ArrayDisplay($Array)
    $R = Random(0, 9, 1)
    If Not $Array[0][$R] = $R Then
    ToolTip("Array:" & $R)
    $Array[0][$R] = $R
    $Array[1][0] += 1

    Return $R
    Else
    For $X = 0 To 9
    If $Array[0][$X] = "" Then
    ToolTip("Array:2" & $X)
    $Array[0][$X] = $X
    $Array[1][0] += 1
    Return $X
    EndIf
    Next
    EndIf
    _ArrayDisplay($Array)
    EndFunc   ;==>_RandomizeNumbers

    M = 1
    O = 0
    N = 6
    E = 5
    Y = 2

    D = 7, R = 8, and S = 9.


    Func _RandomizeNumbers()
    If $Array[1][0] = 10 Then Global $Array[2][10] = [["", "", "", "", "", "", "", "", ""],[0]]
    $R = Random(0, 9, 1)
    If Not $Array[0][$R] = $R and $Array[0][$R] = "" Then
    $Array[0][$R] = $R
    $Array[1][0] += 1
    ConsoleWrite("New Random Number: " & $Array[0][$R]&@CRLF)
    Else
    ConsoleWrite("Old Random Number: " & $R&@CRLF)
    For $X = 0 To 9
    If $Array[0][$X] = "" Then
    $Array[0][$X] = $X
    ConsoleWrite("New Random Number: " & $Array[0][$R]&@CRLF)
    $Array[1][0] += 1
    $R = $X
    ExitLoop
    EndIf
    Next
    EndIf
    ;_ArrayDisplay($Array)
    Return $R
    EndFunc   ;==>_RandomizeNumbers


    If StringLen(StringMid($Send, 4, 1) + StringMid($More, 4, 1)) > 1 Then
    $Y = StringTrimLeft(StringMid($Send, 4, 1) + StringMid($More, 4, 1), 1)
    Else
    $Y = StringMid($Send, 4, 1) + StringMid($More, 4, 1)
    EndIf
#ce

I'm really unhappy with it, I cheat, and it's not brute force. Can anybody push me in the right direction?

Wikipedia say that I could use mod to help me work it out, but my maths isn't so strong :mellow: .

http://en.wikipedia.org/wiki/Verbal_arithmetic

Help me Obi Wan Kenobi,

You're my only hope.

Edited by BitByteBit

Share this post


Link to post
Share on other sites
muhmuuh

Hi!

You can bruteforce all combinations for Send and More and than see if the result could match Money. To bruteforce Send and Money you will have to generate all possible numbers for s,e,n,d,m,o,y; which is less than 10^7 combinations. Example

for $s=0 to 9
 for $e=0 to 9
  for $n=0 to 9
   ...
        for $y=0 to 9
         $send=$s*1000+$e*100+$n*10+$d
         same for $more
         $money=$send+$more
         
         and you check if the numbers for m,o,n,e match with those you've always generated and check if $y is different from all other letters.

also in the loops you need to make sure that the letters are different.

Good luck!


I ran. I ran until my muscles burned and my veins pumped battery acid. Then I ran some more.

Share this post


Link to post
Share on other sites
Tvern

For a nice challenge you can make a script that would work for other challenges like this. I've started making one myself :mellow:

Edit: And came up with this.

#include <Array.au3>
$script = _BuildScript("Send","More","Money")
$array = _RunScript($script)
_ArrayDisplay($array)

; #FUNCTION# ====================================================================================================================
; Name...........: _BuildScript
; Description ...: Returns a script to evaluate possible values of the letters in 3 given strings
; Syntax.........: _BuildScript($sWord1, $sWord2, $sResult[, $nBase = 10[, $bUnique = True[, $canbe0 = False]]])
; Parameters ....: $sWord1 - The first string to evaluate
;   $sWord2 - The second string to evaluate
;   $sResult - The sum of $sWord1 and $sWord2
;   $nBase - [optional] the system to use. (Default is 10, for decimal) !!!!not working yet!!!!
;   $bUnique - [optional] Flag to indicate if numbers represent an unique number, or if doubles are allowed (Default is True, for unique numbers)
;   $canbe0 - [optional] Flag to indicate if the first letter of each string can be 0 (Default is False, the fist letter has to be at least 1)
; Return values .: Success - Returns working script
;   Failure - Not so lucky
; Author ........: Tom
; Remarks .......: Proper errorchecking is not on the ToDo list
; Related .......: _RunScript($script)
; ===============================================================================================================================
Func _BuildScript($sWord1, $sWord2, $sResult, $nBase = 10, $bUnique = True, $canbe0 = False)
    
    If $nBase <> 10 Then
        MsgBox(0,"error","base" & $nBase & " is not yet implemented.")
        Return
    EndIf
    
    Local $script, $tabsstring, $ScriptEnd
    Local $nWord1, $nWord2, $nResult
    $sWord1 = StringLower($sWord1)
    $sWord2 = StringLower($sWord2)
    $sResult = StringLower($sResult)
    $aLetters = _UniqueLetters($sWord1&$sWord2&$sResult)
    
    ;create the embedded For...To...Next loops
    $script = "Local $Return, $Word1, $Word2, $Result" & @CRLF
    For $i = 1 To $aLetters[0]
        $tabsstring = "" 
        For $i0 = 2 To $i ;proper indentation because it's nice
            $tabsstring &= @TAB
        Next
        $script &= $tabsstring
        
        ;conditional based on the value of $canbe0
        Switch $aLetters[$i]
            Case $canbe0
                $script &= "For $" & $aLetters[$i] & " = 0 To " & $nBase-1 & @CRLF ;first letter is allowed to be 0
            Case StringLeft($sWord1,1), StringLeft($sWord2,1), StringLeft($sResult,1)
                $script &= "For $" & $aLetters[$i] & " = 1 To " & $nBase-1 & @CRLF ;this is a first letter and not allowed to be 0
            Case Else
                $script &= "For $" & $aLetters[$i] & " = 0 To " & $nBase-1 & @CRLF ;this is not a first letter
        EndSwitch
        
        ;conditional based on the value of $bUnique
        ;create a switch statement to "continueloop" if another letter already has this value.
        If $bUnique And ($i > 1) Then
            $script &= $tabsstring & @TAB
            $script &= "Switch $" & $aLetters[$i] & @CRLF
            $script &= $tabsstring & @TAB & @TAB
            $script &= "Case "
            For $i1 = 1 To $i-1
                $script &= "$" & $aLetters[$i1]
                If $i1 < $i-1 Then
                    $script &= ", "
                Else
                    $script &= @CRLF
                EndIf
            Next
            $script &= $tabsstring & @TAB & @TAB & @TAB
            $script &= "ContinueLoop" & @CRLF
            $script &= $tabsstring & @TAB
            $script &= "EndSwitch" & @CRLF
        EndIf
        
        ;this creates a properly tabbed end of the script to be added when the script is complete.
        $ScriptEnd = $tabsstring & "Next" & @CRLF & $ScriptEnd
    Next

    ;This is the code that checks if $Word1 + $Word2 = $Result
    ;Convert Words to Numbers
    ;faster then Number($w&$o&$r&$d)
    For $i = 1 To StringLen($sWord1)
         $nWord1 &= "$" & StringMid($sWord1,$i,1) & "*" & 10^(StringLen($sWord1)-($i*1))
         If $i < StringLen($sWord1) Then $nWord1 &= "+"
    Next
    $script &= $tabsstring & @TAB
    $script &= "$Word1 = " & $nWord1 & @CRLF
    For $i = 1 To StringLen($sWord2)
         $nWord2 &= "$" & StringMid($sWord2,$i,1) & "*" & 10^(StringLen($sWord2)-($i*1))
         If $i < StringLen($sWord2) Then $nWord2 &= "+"
    Next
    $script &= $tabsstring & @TAB
    $script &= "$Word2 = " & $nWord2 & @CRLF
    For $i = 1 To StringLen($sResult)
         $nResult &= "$" & StringMid($sResult,$i,1) & "*" & 10^(StringLen($sResult)-($i*1))
         If $i < StringLen($sResult) Then $nResult &= "+"
    Next
    $script &= $tabsstring & @TAB
    $script &= "$Result = " & $nResult & @CRLF

    ;Compare Numbers
    $script &= $tabsstring & @TAB
    $script &= "If $Word1+$Word2 = $Result Then" & @CRLF
    $script &= $tabsstring & @TAB & @TAB
    $script &= "$Return &= $Word1 & '+' & $Word2 & '=' & $Result & ','" & @CRLF
    $script &= $tabsstring & @TAB
    $script &= "Endif" & @CRLF
    $script &= $ScriptEnd
    $script &= "ConsoleWrite($Return)"
    Return $script
EndFunc
; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: _UniqueLetters
; Description ...: Returns an array of unique letters found in a string
; Syntax.........: _UniqueLetters($string)
; Parameters ....: $string - The string to get the unique letters from
; Return values .: Success - A 1-based array of letters with $array[0] containging the count
; Author ........: Tom
; Remarks .......: This function is used internally by _BuildScript.
; Related .......: _BuildScript
; ===============================================================================================================================
Func _UniqueLetters($string)
    Local $newstring, $letter
    For $i = 0 To StringLen($string)
        $letter = StringMid($string,$i,1)
        If StringInStr($newstring,$letter) Then ContinueLoop
        $newstring &= $letter
    Next
    Return StringSplit($newstring,"")
EndFunc
; #FUNCTION# ====================================================================================================================
; Name...........: _RunScript
; Description ...: Runs a script created by _BuildScript() and returns the output as an array
; Syntax.........: _RunScript($script)
; Parameters ....: $script - The script to execute
; Return values .: Success - 1-based array with results
;   Failure - Not so lucky
; Author ........: Tom
; Remarks .......: Proper errorchecking is not on the ToDo list
; Related .......: _BuildScript
; ===============================================================================================================================
Func _RunScript($script)
    Local $path = @TempDir & "\SolveIt.au3", $line, $PID
    FileWrite($path,$script)
    $PID = Run(@AutoItExe & ' /AutoIt3ExecuteScript "' & $path & '"',@ScriptDir,@SW_HIDE,0x02)
    While 1
        $line &= StdoutRead($PID)
        If @error Then ExitLoop
    Wend
    FileDelete($path)
    $line = StringTrimRight($line,1)
    Return StringSplit($line,",")
EndFunc

It generates a custom function for the given parameters and then executes that through @AutoItExe as $STDOUT_CHILD.

Not sure if people will approve of this method, but I was bored and this was something new for me.

Edited by Tvern

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  

×