4 votos

Applescript - Código para resolver el rompecabezas 'Safe Cracker' del Daily Telegraph

No sé si a alguien le va a interesar esto (¡o si es el lugar correcto para publicarlo!). Pasé un par de horas intentando escribir un script</strkeep><strkeep> que resolviera el puzzle 'Safe Cracker' del periódico. Mi código funcionó pero, como verás, es muy torpe y no está bien escrito (¡además, algunas de mis matemáticas son sospechosas!). No quiero que me critiquen el código porque sólo era una prueba para ver si podía hacerlo, pero me interesaría mucho ver si alguien ha hecho un trabajo mejor.

The Puzzle

set ListNoZeros to {}
set ListNoRepeats1 to {}
set ListNoRepeats2 to {}
set ListNoRepeats3 to {}
set ListNoRepeats4 to {}
set ListNoRepeats5 to {}
set ListNoRepeats6 to {}
set ListNoRepeats7 to {}
set ListNoRepeats8 to {}
set ListNoRepeats9 to {}
set ListNoRepeats10 to {}
set ListNoRepeats11 to {}
set ListNoRepeats12 to {}
set ListNoRepeats13 to {}
set ListNoRepeats14 to {}
set ListNoRepeats15 to {}
set ListNoRepeats16 to {}
set ListAnswer to {}

repeat with a from 1234 to 9876

   --split the number into seperate digits
   set theNumber to a as string

   set String1 to characters 1 thru 1 of theNumber
   set String2 to characters 2 thru 2 of theNumber
   set String3 to characters 3 thru 3 of theNumber
   set String4 to characters 4 thru 4 of theNumber

   set Num1 to String1 as number
   set Num2 to String2 as number
   set Num3 to String3 as number
   set Num4 to String4 as number

   -- Remove all zeros
   if Num1 is not 0 and Num2 is not 0 and Num3 is not 0 and Num4 is not 0 then copy a to the end of ListNoZeros -- Number of posibilities = 6357
end repeat

-- Is each number different? (Testing 1 & 2)
repeat with b from 1 to length of ListNoZeros
   set theCurrentListItem to item b of ListNoZeros

   --split the number into seperate digits
   set theNumber to theCurrentListItem as string

   set String1 to characters 1 thru 1 of theNumber
   set String2 to characters 2 thru 2 of theNumber
   set String3 to characters 3 thru 3 of theNumber
   set String4 to characters 4 thru 4 of theNumber

   --Convert back to number
   set Num1 to String1 as number
   set Num2 to String2 as number
   set Num3 to String3 as number
   set Num4 to String4 as number

   if Num1 / Num2 is not equal to 1 then copy theCurrentListItem to the end of ListNoRepeats1 -- Number of posibilities = 5790
end repeat

-- Is each number different? (Testing 1 & 3)
repeat with c from 1 to length of ListNoRepeats1
   set theCurrentListItem to item c of ListNoRepeats1

   --split the number into seperate digits
   set theNumber to theCurrentListItem as string

   set String1 to characters 1 thru 1 of theNumber
   set String2 to characters 2 thru 2 of theNumber
   set String3 to characters 3 thru 3 of theNumber
   set String4 to characters 4 thru 4 of theNumber

   --Convert back to number
   set Num1 to String1 as number
   set Num2 to String2 as number
   set Num3 to String3 as number
   set Num4 to String4 as number

   if Num1 / Num3 is not equal to 1 then copy theCurrentListItem to the end of ListNoRepeats2 -- Number of posibilities = 5160
end repeat

-- Is each number different? (Testing 1 & 4)
repeat with d from 1 to length of ListNoRepeats2
   set theCurrentListItem to item d of ListNoRepeats2

   --split the number into seperate digits
   set theNumber to theCurrentListItem as string

   set String1 to characters 1 thru 1 of theNumber
   set String2 to characters 2 thru 2 of theNumber
   set String3 to characters 3 thru 3 of theNumber
   set String4 to characters 4 thru 4 of theNumber

   --Convert back to number
   set Num1 to String1 as number
   set Num2 to String2 as number
   set Num3 to String3 as number
   set Num4 to String4 as number

   if Num1 / Num4 is not equal to 1 then copy theCurrentListItem to the end of ListNoRepeats3 -- Number of posibilities = 4588
end repeat

-- Is each number different? (Testing 2 & 3)
repeat with e from 1 to length of ListNoRepeats3
   set theCurrentListItem to item e of ListNoRepeats3

   --split the number into seperate digits
   set theNumber to theCurrentListItem as string

   set String1 to characters 1 thru 1 of theNumber
   set String2 to characters 2 thru 2 of theNumber
   set String3 to characters 3 thru 3 of theNumber
   set String4 to characters 4 thru 4 of theNumber

   --Convert back to number
   set Num1 to String1 as number
   set Num2 to String2 as number
   set Num3 to String3 as number
   set Num4 to String4 as number

   if Num2 / Num3 is not equal to 1 then copy theCurrentListItem to the end of ListNoRepeats4 -- Number of posibilities = 4028
end repeat

-- Is each number different? (Testing 2 & 4)
repeat with f from 1 to length of ListNoRepeats4
   set theCurrentListItem to item f of ListNoRepeats4

   --split the number into seperate digits
   set theNumber to theCurrentListItem as string

   set String1 to characters 1 thru 1 of theNumber
   set String2 to characters 2 thru 2 of theNumber
   set String3 to characters 3 thru 3 of theNumber
   set String4 to characters 4 thru 4 of theNumber

   --Convert back to number
   set Num1 to String1 as number
   set Num2 to String2 as number
   set Num3 to String3 as number
   set Num4 to String4 as number

   if Num2 / Num4 is not equal to 1 then copy theCurrentListItem to the end of ListNoRepeats5 -- Number of posibilities = 3526
end repeat

-- Is each number different? (Testing 3 & 4)
repeat with g from 1 to length of ListNoRepeats5
   set theCurrentListItem to item g of ListNoRepeats5

   --split the number into seperate digits
   set theNumber to theCurrentListItem as string

   set String1 to characters 1 thru 1 of theNumber
   set String2 to characters 2 thru 2 of theNumber
   set String3 to characters 3 thru 3 of theNumber
   set String4 to characters 4 thru 4 of theNumber

   --Convert back to number
   set Num1 to String1 as number
   set Num2 to String2 as number
   set Num3 to String3 as number
   set Num4 to String4 as number

   if Num3 / Num4 is not equal to 1 then copy theCurrentListItem to the end of ListNoRepeats6 -- Number of posibilities = 3024
end repeat

-- The 4th is 3 more than the 3rd
repeat with h from 1 to length of ListNoRepeats6
   set theCurrentListItem to item h of ListNoRepeats6

   --split the number into seperate digits
   set theNumber to theCurrentListItem as string

   set String1 to characters 1 thru 1 of theNumber
   set String2 to characters 2 thru 2 of theNumber
   set String3 to characters 3 thru 3 of theNumber
   set String4 to characters 4 thru 4 of theNumber

   --Convert back to number
   set Num1 to String1 as number
   set Num2 to String2 as number
   set Num3 to String3 as number
   set Num4 to String4 as number

   if Num4 = Num3 + (3) then copy theCurrentListItem to the end of ListNoRepeats7 -- Number of posibilities = 252
end repeat

-- The 1st and the 3rd differ by 4. First (1st > 3rd)
repeat with i from 1 to length of ListNoRepeats7
   set theCurrentListItem to item i of ListNoRepeats7

   --split the number into seperate digits
   set theNumber to theCurrentListItem as string

   set String1 to characters 1 thru 1 of theNumber
   set String2 to characters 2 thru 2 of theNumber
   set String3 to characters 3 thru 3 of theNumber
   set String4 to characters 4 thru 4 of theNumber

   --Convert back to number
   set Num1 to String1 as number
   set Num2 to String2 as number
   set Num3 to String3 as number
   set Num4 to String4 as number

   if (Num3 - Num1) = 4 then copy theCurrentListItem to the end of ListNoRepeats8 -- Number of posibilities = 12
end repeat

-- The 1st and the 3rd differ by 4. Second (1st < 3rd)
repeat with j from 1 to length of ListNoRepeats7 -- Using this list twice.....
   set theCurrentListItem to item j of ListNoRepeats7

   --split the number into seperate digits
   set theNumber to theCurrentListItem as string

   set String1 to characters 1 thru 1 of theNumber
   set String2 to characters 2 thru 2 of theNumber
   set String3 to characters 3 thru 3 of theNumber
   set String4 to characters 4 thru 4 of theNumber

   --Convert back to number
   set Num1 to String1 as number
   set Num2 to String2 as number
   set Num3 to String3 as number
   set Num4 to String4 as number

   if (Num1 - Num3) = 4 then copy theCurrentListItem to the end of ListNoRepeats9 -- Number of posibilities = 30
end repeat

-- Join these 2 lists together
set ListNoRepeats10 to ListNoRepeats8 & ListNoRepeats9 -- Number of posibilities = 42

-- The sum of the middle two is divisible by 5
repeat with k from 1 to length of ListNoRepeats10
   set theCurrentListItem to item k of ListNoRepeats10

   --split the number into seperate digits
   set theNumber to theCurrentListItem as string

   set String1 to characters 1 thru 1 of theNumber
   set String2 to characters 2 thru 2 of theNumber
   set String3 to characters 3 thru 3 of theNumber
   set String4 to characters 4 thru 4 of theNumber

   --Convert back to number
   set Num1 to String1 as number
   set Num2 to String2 as number
   set Num3 to String3 as number
   set Num4 to String4 as number

   if (Num2 + Num3) = 5 or (Num2 + Num3) = 10 then copy theCurrentListItem to the end of ListNoRepeats11 -- Number of posibilities = 7
end repeat

-- Exactly one of the first 2 is odd (First & Second)
repeat with l from 1 to length of ListNoRepeats11
   set theCurrentListItem to item l of ListNoRepeats11

   --split the number into seperate digits
   set theNumber to theCurrentListItem as string

   set String1 to characters 1 thru 1 of theNumber
   set String2 to characters 2 thru 2 of theNumber
   set String3 to characters 3 thru 3 of theNumber
   set String4 to characters 4 thru 4 of theNumber

   --Convert back to number
   set Num1 to String1 as number
   set Num2 to String2 as number
   set Num3 to String3 as number
   set Num4 to String4 as number

   if Num1 mod 2 = 0 and Num2 mod 2 = 1 then copy theCurrentListItem to the end of ListNoRepeats12

   if Num1 mod 2 = 1 and Num2 mod 2 = 0 then copy theCurrentListItem to the end of ListNoRepeats13

   -- Join these 2 lists together
   set ListNoRepeats14 to ListNoRepeats12 & ListNoRepeats13 -- Number of posibilities = 3
end repeat

-- 2nd or 4th are square (but not both)
repeat with m from 1 to length of ListNoRepeats14
   set theCurrentListItem to item m of ListNoRepeats14

   set theNumber to theCurrentListItem as string

   set String1 to characters 1 thru 1 of theNumber
   set String2 to characters 2 thru 2 of theNumber
   set String3 to characters 3 thru 3 of theNumber
   set String4 to characters 4 thru 4 of theNumber

   --Convert back to number
   set Num1 to String1 as number
   set Num2 to String2 as number
   set Num3 to String3 as number
   set Num4 to String4 as number

   if Num2 = 1 or Num2 = 4 or Num2 = 9 then copy theCurrentListItem to the end of ListNoRepeats15

   if Num4 = 1 or Num4 = 4 or Num4 = 9 then copy theCurrentListItem to the end of ListNoRepeats16

   -- Join these 2 lists together
   set ListAnswer to ListNoRepeats15 & ListNoRepeats16
end repeat

display dialog "The Correct Code is - " & ListAnswer ```

5voto

Ignorando mi propio comentario acerca de que esto está al borde de lo offtopc, aquí hay una versión (más bien de fuerza bruta) de Swift que hace un bucle sobre todas las combinaciones posibles una vez.

func is_square(_ i: Int) -> Bool {
    return (i == 1) || (i == 4) || (i == 9)
}

func is_odd(_ i: Int) -> Bool {
    return (i % 2) == 1
}

func xor(_ b1: Bool, _ b2: Bool) -> Bool {
     return ((b1 && !b2) || (!b1 && b2))
}

for first in 1...9 {
    for second in 1...9 {
        for third in 1...9 {
            for fourth in 1...9 {
                if xor(is_square(second), is_square(fourth)) // 1 Either the second or fourth is square, but not both
                   && (third + 3 == fourth)                  // 2 The fourth is three more than the third
                   && xor(is_odd(first), is_odd(second))     // 3 Exactly one of the first two is odd
                   && (abs(first - third) == 4)              // 4 The first and third differ by four
                   && ((second + third) % 5 == 0)            // 5 The sum of the middle two is divisible by five
                {
                    print("\(first)\(second)\(third)\(fourth)")
                }
            }
        }
    }
}

O bien se empieza con la lista de todas las combinaciones y luego se aplica un filtro para determinar la válida (sin comprobación de errores en este caso, pues ya sabemos que habrá exactamente una solución).

func digits(_ i: Int) -> (Int, Int, Int, Int) {
     let d1 = (i / 1000)
     let d2 = (i / 100) % 10
     let d3 = (i / 10) % 10
     let d4 = i % 10
     return (d1, d2, d3, d4)
}

let result = Array(1111...9999).filter({ (code: Int) -> Bool in
    let (first, second, third, fourth) = digits(code)
    return xor(is_square(second), is_square(fourth))
           && (third + 3 == fourth)
           && xor(is_odd(first), is_odd(second))
           && (abs(first - third) == 4)
           && ((second + third) % 5 == 0)
})[0]
print("\(result)")

PD: No he añadido una comprobación de la diferencia de dígitos porque me daba pereza :-) (pero mirando las reglas, una combinación en la que los dígitos son iguales no coincide de todos modos)

3voto

siva Puntos 23

Realización de un bucle y salida anticipada, con mejoras en la comprobación de condiciones. Definitivamente, AppleScript no es el lenguaje que uno elegiría habitualmente para este tipo de problemas.

set squarenumbers to {1, 4, 9}
repeat with n from 1234 to 9876
try
        -- split digits
        set nstr to n as string
        set n1 to character 1 of nstr as number
        set n2 to character 2 of nstr as number
        set n3 to character 3 of nstr as number
        set n4 to character 4 of nstr as number
        -- check distinct digits and for conditions 1 through 5
        if ( ¬
                n1 = n2 or n1 = n3 or n1 = n4 or n2 = n3 or n2 = n3 or n3 = n4 or ¬
                not ((squarenumbers contains n2 and squarenumbers does not contain n4) or (squarenumbers does not contain n2 and squarenumbers contains n4)) or ¬
                not (n3 + 3 = n4) or ¬
                not ((n1 mod 2 = 0 and n2 mod 2 = 1) or (n1 mod 2 = 1 and n2 mod 2 = 0)) or ¬
                not ((n1 - n3) = 4 or (n3 - n1) = 4) or ¬
                not ((n2 + n3) mod 5 = 0) ¬
        ) then
                error 0
        end if
        -- found
        return n
end try
end repeat

Sustitución de display dialog con return en su solución y utilizando time osascript _file_ muestra 12,23 segundos para la solución de la pregunta y 1,25 segundos para mi solución. Es posible realizar más optimizaciones en la iteración.

2voto

Ana Puntos 71

Creo que este es uno de los ejemplos en los que escribir el código llevará mucho más tiempo que resolverlo en el papel :-). Creo que este tipo de solución es lo que se espera para resolver los rompecabezas en los periódicos.

Algoritmo de papel y lápiz:

determinar los posibles números tercero y cuarto

fourth is three more than third

El 9 es el mayor número posible, por lo que el tercer número máximo es 9-3 = 6, por lo que los posibles terceros y cuartos números son seis pares: (1, 4), (2, 5), (3, 6), (4, 7), (5, 8), (6, 9)

determinar el posible segundo número

either second or fourth is square but not both

sum of middel two (second and third) is divisible by 5

four different digits

Las casillas posibles son 1, 4, 9. Repasa los 6 pares y si el cuarto número es cuadrado averigua si hay un número que haga la suma divisible por 5; si el cuarto no es cuadrado el segundo número debe ser uno de los cuadrados y averigua si uno de ellos ofrece divisibilidad por 5.

Esto deja un triplete que cumple las condiciones (1, 4, 7)

determinar el primer número

exactly on of the first two is odd

the first and third differ by four

Como el tercer número es 4 y el 0 no está permitido entonces debe ser 8. Como el segundo número es impar, entonces el 8 satisface la condición.

EDIT: se ha añadido la implementación del algoritmo en lenguaje Python:

squares = {1, 4, 9}
digits = set(range(1, 10))
for third, fourth in ((i, i+3) for i in range(1, 7)):   
    for second in digits - {third, fourth}:             
        if (second + third) % 5 == 0:
            if fourth in squares and second in squares:
                continue
            if squares.intersection({second, fourth}):
                for first in digits - {second, third, fourth}:
                    if abs(first - third) == 4 and (first + second) % 2 != 0:
                        print(f'{first}{second}{third}{fourth}')

2voto

Aaron F Puntos 121

Aquí hay un método escrito en Python que es ineficiente pero sencillo de entender porque no hay bucles anidados: intenta números aleatorios entre 1111 y 9999 hasta que encuentra la respuesta correcta:

from random import randint

# Loop forever:
while True:  
 # Assign a random number between 1 and 9 to the variables a, b, c, and d:
 a,b,c,d = [randint(1,9) for i in range(4)]

 # "5 The sum of the middle two is divisible by five."
 if (b + c) % 5 != 0:  
  continue

 # "2 The fourth is three more than the third."
 if c + 3 != d:  
  continue

 # "3 Exactly one of the first two is odd."
 if (a % 2 == 0 and b % 2 == 0) or (a % 2 == 1 and b % 2 == 1):
  continue

 # "1 Either the second or the fourth is square, but not both."
 if (b in [1,4,9] and d in [1,4,9]) or (b not in [1,4,9] and d not in [1,4,9]):
  continue

 # "4 The first and third differ by four."
 if (abs(a-c) != 4):
  continue

 # Print the answer:
 print(a,b,c,d)

 # Exit the loop:
 break

(se podría utilizar el AND lógico para la comprobación de pares/impares, y el XOR para la comprobación de cuadrados, pero esta forma es más fácil de leer para los no programadores)

AppleAyuda.com

AppleAyuda es una comunidad de usuarios de los productos de Apple en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X