Dibuja un hexaglifo aleatorio

23

ingrese la descripción de la imagen aquí

La imagen de arriba se llama hexa-glifo. Los hexaglifos son algunos patrones geniales que inventé mientras hacía garabatos durante mi clase DiffEq. Así es como haces uno:

  1. Considere el siguiente conjunto de puntos, con forma de hexagrama regular. El hexágono interno es el que contendrá el glifo final, mientras que los 6 puntos externos forman una estrella y es donde comenzaremos a dibujar nuestras líneas.

ingrese la descripción de la imagen aquí

  1. De los seis puntos externos, seleccione aleatoriamente un par. Para mayor eficiencia, debería haber al menos otro punto entre los dos puntos seleccionados (de lo contrario, no tendría ningún efecto en la figura final). Luego, desde cada uno de los dos puntos, lanza un rayo hacia el otro. Este rayo está bloqueado por líneas anteriores.

ingrese la descripción de la imagen aquí

  1. Repita este proceso hasta que se hayan formado los 9 bordes, como se muestra en las siguientes imágenes.

ingrese la descripción de la imagen aquí

  1. Aquí hay un ejemplo de rayos bloqueados. Los extremos del segmento del rayo todavía son visibles, pero la porción central está ocluida por los dos primeros segmentos que dibujamos.

ingrese la descripción de la imagen aquí

  1. Estos dos rayos también están "bloqueados", pero esto no causa ninguna diferencia visible porque están bloqueados por la misma otra línea.

ingrese la descripción de la imagen aquí

  1. Avance rápido hasta que se dibujan las 9 líneas. Si desea una explicación más detallada de estos pasos omitidos, puedo exponer.

ingrese la descripción de la imagen aquí

  1. Finalmente, elimina los puntos de la estrella. Para que parezca más bonito, también se eliminan los puntos gruesos.

ingrese la descripción de la imagen aquí

El reto

Su desafío es generar una representación visual de un hexaglifo aleatorio. Este es el código de golf, gana la menor cantidad de bytes.

  1. Todos los hexaglifos posibles deben aparecer con alguna probabilidad positiva. Se generan diferentes hexaglifos al cambiar el orden en que se dibujan los 9 bordes.

  2. Además, todas las imágenes producidas por su programa deben ser hexaglifos válidos. Ciertos patrones (como un esquema completo del hexágono interno) no pueden aparecer como un hexaglifo, por lo que el programa no debe generarlos.

  3. El resultado debe ser una imagen gráfica (impresa en pantalla o archivo).

  4. El hexágono debe ser regular, pero puede aparecer en cualquier orientación.

  5. Las reflexiones / rotaciones no se consideran únicas. (Esto podría hacer que el requisito 1 sea más fácil de seguir).

PhiNotPi
fuente
8
I made up while doodling during my DiffEq class. La forma en que ocurren todos los grandes descubrimientos ...: P
Rɪᴋᴇʀ
¿Cuáles son los requisitos mínimos para la imagen? ¿Hasta qué punto debe ser reconocible un arte ASCII siempre que cada borde esté representado y colocado vagamente en el lugar correcto?
John Dvorak
@ JanDvorak Eliminé la opción de arte ASCII del desafío (como dentro de los 2 minutos posteriores a la publicación) porque los programas que producen arte ASCII y salidas gráficas no son fácilmente comparables.
PhiNotPi
¿Qué pasa con el pixel art entonces? Un encabezado PPM no es demasiado pesado, y luego la única diferencia es usar '01'espacio intercalado en lugar de ' *'.
John Dvorak
@JanDvorak Output sería un archivo de imagen con el formato correcto, ¿verdad? Entonces no veo nada malo en ello.
PhiNotPi

Respuestas:

18

Mathematica, 273 268 264 242 bytes

c=CirclePoints;b@_=k=1>0;Graphics[Line/@Cases[Append[Join@@({c@6,{3^.5/2,-Pi/6}~c~6}),{0,0}][[b@#=!k;#]]&/@TakeWhile[#,t=k;(r=t;t=b@#;r)&]&/@Join@@RandomSample[{#,Reverse@#}&/@Partition[Range@12,3,2,1]~Join~Array[{2#,13,2#+6}&,3]],{_,__}]]

Se representa como un superíndice Ten Mathematica y es un operador de transposición de postfix.

Resolver los errores en esto tomó una eternidad ... hacia el final, pirateé algunas cosas juntas para que funcionara, así que esto definitivamente es subóptimo. También me pregunto si en general sería mejor implementar la especificación más literalmente a través de las líneas a través del hexágono exterior y dejar que las funciones de geometría de Mathematica manejen las intersecciones.

Tenga en cuenta que este es un programa completo y si desea ejecutar el código varias veces dentro de una sola sesión REPL, deberá prefijarlo Clear[b].

Aquí están los resultados de 20 carreras:

ingrese la descripción de la imagen aquí

Explicación

Esta solución no hace uso de los puntos estelares externos en absoluto. En cambio, trabaja directamente con los puntos que forman parte del hexágono y las líneas que cubren tres de ellos a la vez.

Etiquetemos los puntos:

ingrese la descripción de la imagen aquí

1comienza en una esquina ligeramente extraña, pero esto se debe al comportamiento predeterminado (también algo extraño) de CirclePoints. Comenzar el hexágono desde allí resultó más barato.

Ahora queremos encontrar las líneas relevantes a través de tres de esos puntos que corresponden a puntos conectados de la estrella exterior. Los que rodean el hexágono son, por supuesto, solo 3 puntos adyacentes (módulo 12), comenzando desde un número impar. Los del otro lado del centro consisten en un número par n, 13y n+6.

Las representaciones de estas líneas (en forma de listas de tres puntos son generadas por el siguiente código):

Partition[Range@12,3,2,1]~Join~Array[{2#,13,2#+6}&,3]

El Partitiongenera las líneas alrededor del hexágono y Arraylas líneas a través del centro. Para procesar ambas vigas, asignamos esta función sobre la lista de líneas:

{#,Reverse@#}&

Ahora los barajamos RandomSamplepara procesarlos en un orden aleatorio. Join @@aplana la lista de pares para que tengamos una lista de vigas.

Intermedio breve: para realizar un seguimiento de los puntos que ya están bloqueados, utilizamos una función de búsqueda b, que se inicializa Truepara todos los valores mediante b@_=k=1>0;. Al procesar una viga, conservamos todos los puntos hasta el primer punto que tiene b[n] == False( incluido ese):

TakeWhile[#,t=k;(r=t;t=b@#;r)&]&

Siento que esta es la parte más golfa en este momento ... el uso de dos variables temporales para jugar Mastermind parece realmente costoso. De todos modos, el resultado de esto nos da los puntos en una línea que podemos dibujar. Ahora esta función se asigna sobre cada uno de esos puntos:

Append[Join@@({c@6,{3^.5/2,-Pi/6}~c~6}),{0,0}][[b@#=!k;#]]&

La primera parte genera la lista de los 13 puntos utilizando los resultados intercalados de dos llamadas a CirclePoints(con diferentes radios para los centros de los bordes y las esquinas del hexágono). Tenga en cuenta b@#=!kque ahora establece el valor de la tabla de búsqueda para el punto actual, de Falsemodo que ninguna viga más pueda pasar a través de él. Finalmente, el valor se usa como índice en la lista de coordenadas para obtener el punto 2D correcto.

Cases[...,{_,__}]

Esto descarta todas las listas de un solo elemento, ya que se representarían como puntos individuales (y visibles). Finalmente presentamos el resultado:

Graphics[Line/@...]
Martin Ender
fuente
b@_=1>0=b=1>0&
CalculatorFeline
@CatsAreFluffy No creo que funcione, porque necesito poder sobrescribir valores individuales más adelante.
Martin Ender
Buen uso de CirclePoints.
DavidC
Aprecié ese enlace de Youtube.
DanTheMan
8

Zapatos (Ruby) Rev C 184 bytes

Se ahorran 12 bytes transfiriendo la responsabilidad de verificar si una media línea particular se debe dibujar del programa principal al método de dibujo. Sin embargo, el programa principal todavía tiene que verificar si toda la línea está completamente bloqueada.

Shoes.app{t=[]
d=->p,q{t[p]&&t[q]||line(p/6*8,p%6*14,q/6*8,q%6*14)}
%w{1I IW WM M5 5' '1 =A P. R,}.shuffle.map{|i|b=i.sum/2
c=b*2-a=i.ord
t[a]&&t[c]||(d[a,b]
d[b,c]
t[a]=t[b]=t[c]=1)}}

Zapatos (Ruby) 205 ... Rev B 196 bytes

Shoes es una herramienta basada en rubíes para crear GUI, etc. Es la primera vez que la uso. mothereff.in/byte-counter cuenta mi envío como 196 bytes, pero por alguna razón Shoes lo cuenta como 202.

Además, Ruby te permite hacer cosas como, t[a=i.ord]pero extrañamente, parece que no funciona como se esperaba con Shoes.

Shoes.app{t=[]
d=->p,q{line(p/6*8,p%6*14,q/6*8,q%6*14)}
%w{1I IW WM M5 5' '1 =A P. R,}.shuffle.map{|i|b=i.sum/2
c=b*2-a=i.ord
t[a]&&t[c]||(t[a]&&t[b]||d[a,b]
t[b]&&t[c]||d[b,c]
t[a]=t[b]=t[c]=1)}}

Explicación

No considero las partes de la línea fuera del hexágono. Solo dibujo la parte que necesita ser dibujada. Lo importante es si las líneas cruzan las intersecciones (si solo dibujamos las partes que deben dibujarse, esto significa que comienzan / terminan en las intersecciones).

La regla básica es que si se han visitado los dos puntos finales de una línea, la línea está bloqueada y no debe dibujarse. Como las líneas se dibujan en dos mitades, también tenemos que verificar si se ha visitado el punto medio para ver si cada mitad se debe dibujar o no.

Llevo un registro de los puntos que se han visitado en la matriz t[]. Esto termina conteniendo una entrada para cada coordenada física en la cuadrícula a continuación. No hay una matriz lógica de 13 elementos separada. Al final, t[]puede tener 87 elementos, aunque solo hasta 13 contendrán datos útiles.

Internamente, las coordenadas de los puntos finales de las líneas están dadas por un solo número z, donde z% 6 es la coordenada y y z / 6 es la coordenada x. En este sistema, el hexágono se aplana. Cuando se trazan las líneas, la escala x se multiplica por 8 y la escala y se multiplica por 14, que es una aproximación racional muy cercana a la relación correcta: 14/8 = 1.75 vs sqrt (3) = 1.732.

El sistema de coordenadas interno se muestra a continuación, con algunas salidas de muestra.

ingrese la descripción de la imagen aquí

Sin golf

Shoes.app{
  t=[]                                          #Empty array for status tracking
  d=->p,q{line(p/6*8,p%6*14,q/6*8,q%6*14)}      #Drawing method. Convert p and q into x,y pairs, scale and draw line.
  %w{1I IW WM M5 5' '1 =A P. R,}.shuffle.map{|i|#take an array of the coordinates of the endpoints of each line, shuffle, then for each line
    b=i.sum/2                                   #b = midpoint of line, convert ASCII sum to number (average of the two coordinates)
    a=i.ord                                     #a = first endpoint of line, convert ASCII to number (no need to write i[0].ord)
    c=b*2-a                                     #c = second endpoint of line (calculating is shorter than writing i[1].ord)
    t[a]&&t[c]||(                               #if both endpoints have already been visited, line is completely blocked, do nothing. ELSE
      t[a]&&t[b]||d[a,b]                        #if first endpoint and midpoint have not both been visited, draw first half of line
      t[b]&&t[c]||d[b,c]                        #if second endpoint and midpoint have not both been visited, draw second half of line
      t[a]=t[b]=t[c]=1                          #mark all three points of the line as visited
    )
  }
}

Más salidas de muestra

Estos se hicieron con una versión anterior del programa. La única diferencia es que el posicionamiento del hexágono en la ventana ahora es ligeramente diferente.

ingrese la descripción de la imagen aquí

Level River St
fuente
mothereff.in/byte-counter counts my submission as 196 bytes, but for some reason Shoes counts it as 202.No sé al 100% si esto es cierto, pero creo que la razón por la que Shoes contó su código como 202 bytes en lugar de 196 es porque sus líneas nuevas son en realidad una secuencia de dos caracteres "\ r \ n". Esto hace que cada nueva línea se cuente dos veces. Aquí hay una respuesta de desbordamiento de pila con respecto a \ r y \ n.
K Zhang
Jeje, no puedo superar el nombre de Ruby with Shoes XD
Beta Decay
3

Python, 604 591 574 561 538 531 536 534 528 493 483 452 431 420 419 415 388 385 384 bytes

He adaptado la idea de Level River St de verificar si una línea se bloqueará al verificar si ambos puntos finales de la línea ya han sido visitados anteriormente. Esto ahorra 27 bytes. Sugerencias de golf bienvenidas.

Editar: corrección de errores y golf g(p,q)para 3 bytes. Golfizado Lpor un byte.

from turtle import*
from random import*
R=range
G=goto
*L,=R(9)
shuffle(L)
a=[0]*13
ht()
T=12
c=[(j.imag,j.real)for j in(1j**(i/3)*T*.75**(i%2/2)for i in R(T))]+[(0,0)]
def g(p,q):pu();G(c[p]);a[p]*a[q]or pd();G(c[q])
for m in L:
 p=2*m;x,y,z=R(p,p+3)
 if m<6:
  if a[x]*a[z%T]<1:g(x,y);g(y,z%T);a[x]=a[y]=a[z%T]=1
 else:
  if a[p-11]*a[p-5]<1:g(p-11,T);g(p-5,T);a[p-11]=a[p-5]=a[T]=1

No golfista:

from turtle import*
from random import*

def draw_line(points, p_1, p_2):
    penup()
    goto(points[p_1])
    if not (a[p] and a[q]):
        pendown()
    goto(points[p_2])

def draw_glyph():
    ht()
    nine_lines = list(range(9))
    shuffle(nine_lines)
    size = 12
    center = [0,0]

    points = []
    for i in range(12):      # put in a point of a dodecagon
                             # if i is even, keep as hexagon point
                             # else, convert to hexagon midpoint
        d = 1j**(i/3) * 12   # dodecagon point
        if i%2:
            d *= .75**.5     # divide by sqrt(3/4) to get midpoint
        points += (d.imag, d.real)
    points.append(center)

    a = [0]*13
    for m in nine_lines:
        p = 2*m
        if m<6:
            x, y, z = p, p+1, p+2
            if not (a[x] and a[z%12]):
                draw_line(points, x, y)
                draw_line(points, y, z%12)
                a[x] = a[y] = a[z%12] = 1
        else:
            if not (a[p-11] and a[p-5]):
                draw_line(p-11, 12)
                draw_line(p-5, 12)
                a[p-11] = a[p-5] = a[12] = 1

Los hexaglifos en sí son bastante pequeños, ya que utilizamos un hexágono de 12 píxeles como base (por razones de golf). Aquí hay algunos ejemplos de hexa-glifos (disculpas por el mal cultivo):

Un ejemplo de glifo hexadecimal Un ejemplo de glifo hexadecimal Un ejemplo de glifo hexadecimal Un ejemplo de glifo hexadecimal Un ejemplo de glifo hexadecimal Un ejemplo de glifo hexadecimal

Sherlock9
fuente
Podría guardar algunos bytes:R=range;G=goto
Tim Čas