Maximiza el recorrido del Rey Sudoku

16

Antecedentes

Sudoku es un rompecabezas de números donde, dada una cuadrícula norte×norte dividida en cajas de tamaño norte , cada número de 1 a norte debe aparecer exactamente una vez en cada fila, columna y caja.

En el juego de Ajedrez, el Rey puede moverse a cualquiera de (como máximo) 8 celdas adyacentes en un turno. "Adyacente" aquí significa horizontal, vertical o diagonalmente adyacente.

La gira del Rey es una analogía de la gira del Caballero; Es un camino (posiblemente abierto) que visita cada celda exactamente una vez en el tablero dado con los movimientos del Rey del Ajedrez.

Tarea

Considere una cuadrícula de Sudoku de 6 por 6:

654 | 321
123 | 654
----+----
462 | 135
315 | 246
----+----
536 | 412
241 | 563

y una gira del Rey (de 01a 36):

01 02 03 | 34 35 36
31 32 33 | 04 05 06
---------+---------
30 23 28 | 27 26 07
22 29 24 | 25 09 08
---------+---------
21 19 16 | 10 14 13
20 17 18 | 15 11 12

El recorrido forma el número de 36 dígitos 654654564463215641325365231214123321.

Tomar un tour diferente del Rey da números más grandes; por ejemplo, puedo encontrar un camino que comience con el 65<6>56446556...cual es definitivamente mayor que el anterior. Puedes cambiar el tablero de Sudoku para obtener números aún más altos:

... | ...
.6. | ...
----+----
..6 | ...
.5. | 6..
----+----
.45 | .6.
6.. | 5..

Este tablero incompleto proporciona la secuencia inicial de la 666655546...cual es la secuencia óptima de 9 dígitos iniciales.

Su tarea es encontrar el número más grande para el Sudoku estándar de 9 por 9 con cajas de 3 por 3 , es decir

... | ... | ...
... | ... | ...
... | ... | ...
----+-----+----
... | ... | ...
... | ... | ...
... | ... | ...
----+-----+----
... | ... | ...
... | ... | ...
... | ... | ...

Tenga en cuenta que este desafío no es ; el objetivo es encontrar las soluciones en lugar de escribir un pequeño programa que teóricamente funcione.

Criterio de puntuación y ganador

El puntaje de una presentación es el número de 81 dígitos encontrado por su programa. La presentación con la puntuación más alta gana. Su programa también debe generar la cuadrícula de Sudoku y la gira del Rey en forma legible por humanos; inclúyalos en su envío.

Su programa puede generar múltiples resultados; tu puntaje es el máximo de ellos.

No hay límite de tiempo para su programa. Si su programa continúa ejecutándose y luego encuentra un número mayor, puede actualizar el puntaje del envío editando la publicación. Tiebreaker es el momento más temprano para lograr el puntaje, es decir, el tiempo de publicación (si aún no se ha editado) o el momento de la edición cuando se actualizó el puntaje (de lo contrario).

Bubbler
fuente
2
En su auto nominación de este desafío para Best of PPCG, usted menciona que "Este es probablemente el primer [desafío de código] que solicita directamente la solución optimizada, en lugar de un puntaje combinado con la longitud del código o algo así". Solo quería hacerle saber que eso no es cierto: hay una cadena de salida de laberinto universal más corta que se publicó en 2015.
Esolanging Fruit

Respuestas:

19

Python + Z3 , 999899898789789787876789658767666545355432471632124566352413452143214125313214321, óptimo

Funciona en aproximadamente media hora, produciendo

1 3 4 8 9 7 6 2 5
2 9 7 1 5 6 8 3 4
5 6 8 4 2 3 7 9 1
4 7 6 2 1 5 9 8 3
8 5 1 6 3 9 2 4 7
9 2 3 7 8 4 1 5 6
3 8 5 9 6 1 4 7 2
6 4 9 5 7 2 3 1 8
7 1 2 3 4 8 5 6 9
81 79 78 14 15 16 54 57 56
80 12 13 77 52 53 17 55 58
34 33 11 51 76 75 18  1 59
35 10 32 50 74 72  2 19 60
 9 36 49 31 73  3 71 61 20
 8 48 37 30  4 69 70 62 21
47  7 38  5 29 68 65 22 63
46 43  6 39 28 67 66 64 23
44 45 42 41 40 27 26 25 24
999899898789789787876789658767666545355432471632124566352413452143214125313214321

Código

import z3


def adj(a):
    x, y = a
    for x1 in range(max(0, x - 1), min(9, x + 2)):
        for y1 in range(max(0, y - 1), min(9, y + 2)):
            if (x1, y1) != a:
                yield x1, y1


solver = z3.SolverFor("QF_FD")

squares = list((x, y) for x in range(9) for y in range(9))
num = {(x, y): z3.Int(f"num{x}_{y}") for x, y in squares}
for a in squares:
    solver += 1 <= num[a], num[a] <= 9
for cells in (
    [[(x, y) for y in range(9)] for x in range(9)]
    + [[(x, y) for x in range(9)] for y in range(9)]
    + [
        [(x, y) for x in range(i, i + 3) for y in range(j, j + 3)]
        for i in range(0, 9, 3)
        for j in range(0, 9, 3)
    ]
):
    solver += z3.Distinct([num[x, y] for x, y in cells])
    for k in range(1, 10):
        solver += z3.Or([num[x, y] == k for x, y in cells])

move = {
    ((x0, y0), (x1, y1)): z3.Bool(f"move{x0}_{y0}_{x1}_{y1}")
    for x0, y0 in squares
    for x1, y1 in adj((x0, y0))
}
tour = {(x, y): z3.Int(f"tour{x}_{y}") for x, y in squares}
for a in squares:
    solver += 0 <= tour[a], tour[a] < 81
for a in squares:
    solver += z3.PbEq([(move[a, b], 1) for b in adj(a)] + [(tour[a] == 80, 1)], 1)
for b in squares:
    solver += z3.PbEq([(move[a, b], 1) for a in adj(b)] + [(tour[b] == 0, 1)], 1)
solver += z3.Distinct([tour[a] for a in squares])
for t in range(81):
    solver += z3.Or([tour[a] == t for a in squares])
for a in squares:
    for b in adj(a):
        solver += move[a, b] == (tour[a] + 1 == tour[b])

value = [z3.Int(f"value{t}") for t in range(81)]
for t in range(81):
    solver += 1 <= value[t], value[t] <= 9
for a in squares:
    for t in range(81):
        solver += z3.Implies(tour[a] == t, num[a] == value[t])

assert solver.check() != z3.unsat
opt = 0
while opt < 81:
    model = solver.model()
    for y in range(9):
        print(*(model[num[x, y]] for x in range(9)))
    for y in range(9):
        print(*(f"{model[tour[x, y]].as_long() + 1:2}" for x in range(9)))
    best = [model[value[t]].as_long() for t in range(81)]
    print(*best, sep="")
    print()
    while opt < 81:
        improve = z3.Bool(f"improve{opt}_{best[opt]}")
        solver += improve == (value[opt] > best[opt])
        if solver.check(improve) != z3.unsat:
            break
        solver += value[opt] == best[opt]
        opt += 1
Anders Kaseorg
fuente
Seguramente sobreestimé demasiado el problema. Y olvidé por completo la magia oscura de Z3 ...
Bubbler
@Bubbler está seguro de que una solución óptima está fuera del alcance es difícil. Yo mismo cometí el mismo error, y el mío duró incluso menos tiempo antes de que alguien publicara una solución óptima ... codegolf.stackexchange.com/a/51974/20283
trichoplax
El mío no es salvable, pero me pregunto si este desafío podría funcionar como una variación con un tablero más grande y una pieza de ajedrez diferente (quizás un desafío de seguimiento que se vincula con este)
trichoplax