Visualizar ojos visuales

42

Puede o no recordar Xeyes, un programa de demostración que vino con (y, que yo sepa, todavía viene con) el sistema X Window. Su propósito era dibujar un par de ojos que siguieran el cursor del mouse:

Xeyes

Su desafío es recrear Xeyes con arte ASCII. Escriba un programa o función que dibuje dos ojos artísticos ASCII (especificados a continuación) donde el usuario haga clic y luego mueva sus pupilas para apuntar en la dirección del cursor.

Ojos terminales GIF

El GIF anterior es una grabación de esta implementación de Ruby sin golf , que se puede ejecutar con cualquier versión reciente de Ruby. También puede resultarle útil como referencia para las secuencias de control de Xterm.

Presupuesto

Este es el , por lo que gana la solución con la menor cantidad de bytes.

Se trata de un reto, por lo que su programa debe dibujar usando caracteres ASCII-específicamente, los personajes -, ., |, ', 0, espacio, y la nueva línea. 1 2

Este es un desafío , por lo que su programa debe aceptar entradas y dibujar su salida en tiempo real. 3

Antes de que su programa comience a aceptar entradas, debe inicializar un lienzo en blanco de al menos 20 filas y 20 columnas. No debe dibujar nada hasta que el usuario haga clic en el lienzo.

Cada vez que el usuario hace clic en 4 en el lienzo, el programa debe borrar cualquier resultado anterior y luego dibujar estos ojos ASCII en el lienzo, centrados en el carácter más cercano a la ubicación del cursor del mouse. 5 6 (A continuación, representa el cursor del mouse y no se debe dibujar).

.---. .---.
|   | |   |
|  0|✧|0  |
|   | |   |
'---' '---'

Observe cómo los alumnos "apuntan" hacia el cursor.

Cada vez que el cursor del mouse se mueve sobre el lienzo, el programa debe volver a dibujar las pupilas para que continúen apuntando hacia el cursor, 7 por ejemplo:

             ✧


.---. .---.
|  0| |  0|
|   | |   |
|   | |   |
'---' '---'

Alumno señalando

Supongamos que enumeramos las posiciones de los nueve caracteres internos de cada ojo de la siguiente manera:

.---.
|678|
|591|
|432|
'---'

El alumno será dibujado en uno de los lugares 1- 9. Para decidir cuál, imagine que los caracteres son cuadrados y que el lienzo es una cuadrícula cartesiana, con el centro del 9carácter en (0, 0), el centro de 1en (1, 0), etc. Cuando el programa recibe una entrada, un clic o un movimiento, debe asignar la ubicación de entrada a la coordenada de cuadrícula más cercana 𝑀. Si 𝑀 es (0, 0), la pupila debe dibujarse en (0, 0), es decir, la ubicación del9 anterior. De lo contrario, se debe dibujar como se describe a continuación.

Imagine un plano cartesiano superpuesta a la rejilla y dividido en octantes numerados 1 - 8 :

Si 𝑀 se encuentra dentro del octante 1 , entonces la pupila debe dibujarse en la ubicación de 1arriba, es decir, en (1, 0). Si 𝑀 está en octante 2 , debe dibujarse en 2— y así sucesivamente. Para ilustrar, la siguiente imagen muestra parte de la cuadrícula codificada por colores según el lugar donde se debe dibujar la pupila cuando el cursor del mouse se encuentra en una ubicación particular. Cuando, por ejemplo, el cursor se encuentra en cualquiera de las coordenadas verdes (teniendo en cuenta que las coordenadas de la cuadrícula se encuentran en los centros de los cuadrados, no en sus esquinas), se debe dibujar la pupila 4.

Las pupilas de los dos ojos se mueven independientemente, por lo que para cada ojo repita el proceso con 𝑀 en relación con el centro de ese ojo.

Notas

  1. Este no es un desafío de . La salida debe ser una cuadrícula de caracteres. Por supuesto, puede usar rutinas gráficas para dibujar una cuadrícula de caracteres.

  2. El espacio en blanco puede dibujarse (o, mejor dicho, no dibujarse), sin embargo, es conveniente. Un espacio vacío en la cuadrícula se ve igual que un carácter de espacio y se considerará equivalente.

  3. "Tiempo real" se define aquí como menos de 200 ms entre la entrada y la salida correspondiente que se está dibujando.

  4. Es a su discreción qué botones del mouse se observan para la entrada, y si presionar o soltar constituye un "clic".

  5. El lienzo debe borrarse o se debe lograr el equivalente visual. Con una solución basada en terminal, por ejemplo, imprimir un nuevo lienzo debajo del lienzo anterior no se considera equivalente.

  6. Cuando el usuario hace clic cerca del borde del lienzo de modo que algunos de los caracteres del ojo se dibujarían más allá de su borde, el comportamiento es indefinido. Sin embargo, el programa debe continuar ejecutándose normalmente en los clics posteriores.

  7. Cuando el cursor del mouse sale del "lienzo", el comportamiento no está definido, pero el programa debe continuar ejecutándose normalmente cuando el cursor vuelve a entrar en el lienzo.

  8. Puede aparecer un cursor de texto en el lienzo, siempre que no oscurezca el resultado.

Las lagunas estándar están prohibidas.

Jordán
fuente
2
@ Οurous Dado que cuántos minutos son "unos pocos" en ese caso dependería de la cantidad de memoria que tenga el sistema, y ​​eso podría llevarnos a "esta solución supone que el entorno tiene 512 GB de RAM", voy a decir que potencialmente debe ejecutarse indefinidamente.
Jordan
1
@TaylorScott Nope. Ver nota # 6 (a menos que haya entendido mal su pregunta).
Jordan
1
@ Οurousivo Sí, y no. Si su entorno de destino suele ser uno en el que la fuente predeterminada es monoespacio (por ejemplo, un emulador de terminal o editor de código), está bien. Si el uso de una fuente monoespacial en ese entorno generalmente requiere una configuración adicional (como en una solución JS basada en navegador), esa configuración debe ser parte de su conteo de bytes (por ejemplo, <pre>o font-family:monospace).
Jordan
99
+1 por gran título (o mal título, dependiendo de cómo lo tomes)
FantaC
1
@ Οurous Nope, siempre y cuando no termine inesperadamente.
Jordan

Respuestas:

12

HTML + CSS + JavaScript (ES6), 93 + 19 + 278 276 = 388 bytes

w=7.8125
h=15
with(Math)r=round,
(onclick=e=>F.style=`margin:-3.5em -6.5ch;left:${x=r(e.x/w)*w}px;top:${y=r(e.y/h)*h}px`)({y:-40}),onmousemove=e=>(s=($,o)=>$.style=`left:${a=atan2(Y=r((e.y-y)/h),X=r((e.x-x)/w+o)),X|Y?w*r(cos(a)):0}px;top:${X|Y?h*r(sin(a)):0}px`)(L,3)&&s(R,-3)
*{position:relative
<pre id=F>.---. .---.
|   | |   |
| <a id=L>0</a> | | <a id=R>0</a> |
|   | |   |
'---' '---'

Darrylyeo
fuente
Ambos X||Yse pueden jugar X|Ypara guardar 2 bytes.
Kevin Cruijssen
No funciona tan bien cuando haces clic cerca del fondo del contenedor y tienes que desplazarte hacia abajo. i.stack.imgur.com/s44KU.png No estoy seguro si es específico del contenedor de fragmentos, pero valió la pena mencionarlo.
Draco18s
2
@ Οurous Está redactado de forma bastante ambigua: "centrado en la ubicación del cursor del mouse". ¿"Ubicación" significa "celda de cuadrícula" o puede significar "píxel"? Estoy de acuerdo en que la intención era probablemente la primera, pero la redacción ciertamente parece permitir la segunda.
DLosc
@KevinCruijssen Desafortunadamente, eso no funciona, |termina teniendo prioridad sobre la expresión ternaria.
darrylyeo
@darrylyeo No, no lo hace? : S Esta tabla de precedencia de operadores de JavaScript muestra |y está ||en el mismo nivel, y ambos arriba ?:. Ambos X||Y?w*r(cos(a)):0y X||Y?h*r(sin(a)):0actualmente están en el formulario boolean_condition?A:B. Entonces, cuando cambies X||Ya X|Yél, hará un OR inteligente y luego interpretará como una condición booleana nuevamente. ( (X||Y)?A:Bvs (X|Y)?A:B, no X|(Y?A:B)). Además, no veo ninguna diferencia cuando uso "Copiar fragmento para responder" y cambio el ||a |. Todo sigue funcionando exactamente igual hasta donde puedo decir ..
Kevin Cruijssen
12

Excel VBA, 630 bytes

Subrutina de hoja de trabajo declarada que se ejecuta al hacer clic con el mouse, que no toma ninguna entrada y produce un par de ojos que siguen el cursor. Esto depende de la función auxiliar incluida y la declaración de tipo, que debe colocarse en un módulo normal.

Esta versión está calibrada para ejecutarse con el zoom predeterminado del 100%. Se rompe si intentas desplazarte.

Nota: VBA completa automáticamente la cadena no terminada en la nueva línea, por lo que en el código a continuación, hay tres instancias en las que "se ha incluido un terminal únicamente para resaltar; estos no contribuyen al recuento de bytes

Sub Worksheet_SelectionChange(ByVal t As Range)
With Cells
.Clear
.Font.Name="Courier"'<--- `"` included only for highlighting
.ColumnWidth=1.3
.RowHeight=15
End With
[A1]=" "'<--------------- `"` included only for highlighting
Dim l As p,p As p
GetCursorPos l
While[A1]=" "'<---------- `"` included only for highlighting
DoEvents
GetCursorPos p
For i=0To 1
x=l.x+IIf(i,-56,56)
n=Evaluate("=-Int(-8/Pi()*ATan2("& x-p.x &","& l.y-p.y+0.1 &"))")
n=Asc(-Int(-IIf(Abs(p.x-x)<7And Abs(p.y-l.y)<10,9,IIf(n<-6,8,n)-1)/2)+4)
j=1
For Each c In t.Offset(-2,IIf(i,-5,1)).Resize(5,5)
d=Mid(".---.|567||498||321|'---'",j,1)
c.Value=IIf(d Like"[0-9]",IIf(Asc(d)=n,0," "),"'"&d)
j=j+1
Next c,i
Wend
End Sub

Función auxiliar y declaración de tipo

Declare Sub GetCursorPos Lib"user32"(l As p)
Type p
x As Long
y As Long
End Type

No golfista y comentado

Esta versión está calibrada para ejecutarse a un nivel de zoom del 400%.

''  must be placed in a worksheet code module

''  define this module to run whenever the user either clicks
''  or moves the selection with the arrow keys
Private Sub Worksheet_SelectionChange(ByVal T As Range)

    ''  Declare vars
    Dim refPos  As POSITION, _
        curPos  As POSITION, _
        c       As Range, _
        d       As String, _
        i       As Integer, _
        j       As Integer, _
        n       As Integer, _
        x       As Integer

    ''  Explicitly state that this works only on the
    ''  Worksheet for which this code has been defined
    With Application.ActiveSheet

        ''  Clear eyes and escape var
        Call .Cells.ClearContents

        ''  Define escape var
        Let .[A1] = " "

        ''  Define reference position
        Call GetCursorPos(refPos)

        ''  While not escaped
        Do While [A1] = " "

            ''  Prevent Excel from appearing to freeze
            Call VBA.DoEvents

            ''  Check where the cursor is
            Call GetCursorPos(curPos)

            ''  Iterate over the eyes' indexes
            For i = 0 To 1 Step 1

                ''  Define the reference center of the eye, left first
                Let x = refPos.x + IIf(i, -168, 168)

                '' figure out which of the directions to point the eye and assign that value to `n`
                Let n = Evaluate("=-Int(-8/Pi()*ATan2(" & x - curPos.x & "," & refPos.y - curPos.y + 0.1 & "))")
                Let n = Asc(-Int(-IIf(Abs(curPos.x - x) < 28 And Abs(curPos.y - refPos.y) < 40, 9, IIf(n < -6, 8, n) - 1) / 2) + 4)

                ''  define character index
                Let j = 1

                ''  Iterate over the range in which the eye is to be drawn
                For Each c In T.Offset(-2, IIf(i, -5, 1)).Resize(5, 5)

                    ''  get correct char from the reference data
                    Let d = Mid(".---.|567||498||321|'---'", j, 1)

                    ''  check if the char is a number, if so only keep it if it matches `n`
                    Let c.Value = IIf(d Like "[0-9]", IIf(Asc(d) = n, 0, " "), "'" & d)

                    '' iterate j
                    j = j + 1
            Next c, i
        Loop
    End With
End Sub

Función auxiliar y declaración de tipo

''  Declare the 64-Bit Window API function
Declare PtrSafe Function GetCursorPos Lib "user32" (ByRef posObj As POSITION) As LongLong

''  Define the POSITION type; 0,0 is top left of screen
Type POSITION
x As Long
y As Long
End Type

''  Pre-Operations for optimization
Sub Initialize()
    With Cells

        ''  Define the font as being mono-spaced
        .Font.Name = "Lucida Console"

        ''  Define the size of the cells to be tightly bound around a single char
        .ColumnWidth = 1.5
        .RowHeight = 15
    End With
End Sub

Salida

GIF

Moving_Eyes

Imagen de mayor resolución

Ojos_estáticos

Taylor Scott
fuente
Esto no coincide con la especificación de varias maneras. 1. "Cuadrícula de caracteres" significa caracteres individuales con posiciones distintas. Cuando el cursor del mouse está activado, digamos, el 'carácter más a la derecha de la salida será diferente de cuando está en el 'carácter más a la izquierda . 2. La posición de los ojos no es fija. Un clic del mouse debería hacer que se muevan a la posición en la que se hizo clic. Soy flexible en el método de entrada (aceptaría, por ejemplo, un cursor virtual del mouse controlado por las teclas de flecha), pero hay dos eventos de entrada distintos con un comportamiento distinto: movimiento del mouse y clic del mouse.
Jordan
@ Jordania No estoy muy seguro de lo que quiere decir con el punto 1, ¿podría explicarlo? En cuanto al punto 2, los ojos no son estáticos, y al hacer clic en cualquier celda de la hoja en la que se coloca la subrutina se activará el Worksheet_SelectionChangeevento y pasará el rango de llamada ( Targeto Ten este caso), que vuelve a dibujar los ojos y a *en la llamada celular
Taylor Scott
1
@ Jordania: creo que he abordado todas y cada una de sus preocupaciones, aunque al hacerlo, tuve que limitar mi solución a Excel de 64 bits y estoy trabajando en una versión no comentada y comentada en este momento
Taylor Scott
1
@ Jordania Esto se debe a que las declaraciones de API de Windows para 32 y 64 pero VBA son diferentes, al igual que los detalles de concatenación y exponenciación, donde 32 bits es casi siempre más corto, y actualmente no tengo acceso a una versión de Office de 32 bits: P
Taylor Scott
3
¿Quizás cambiar las dos capturas de pantalla a una pantalla a gif ?
Kevin Cruijssen
7

QBasic ( QB64 ), 361 305 bytes

DO
WHILE _MOUSEINPUT
x=CINT(_MOUSEX)
y=CINT(_MOUSEY)
IF _MOUSEBUTTON(1)THEN l=x-3:k=y
IF(2<l)*(73>l)*(2<k)*(22>k)THEN CLS:FOR i=0TO 1:h=l+6*i:LOCATE k-2,h-2:?".---.":FOR j=1TO 3:LOCATE,h-2:?"|   |":NEXT:LOCATE,h-2:?"'---'":d=x-h:e=y-k:m=ABS(e/d):LOCATE k-SGN(e)*(m>=.5),h-SGN(d)*(m<=2):?"0":NEXT
WEND
LOOP

El clic izquierdo coloca los ojos. Si la colocación de los ojos resultara en parte de los ojos fuera de límites, el programa se "congela" hasta que se realice una colocación válida.

La principal parte difícil es colocar las pupilas. La mayoría de las veces, las coordenadas de la pupila son solo el centro del ojo más (signo (Δx), signo (Δy)), excepto que en los octantes 1 y 5, la coordenada y es igual al centro y, y en octantes 3 y 7, la coordenada x es igual al centro x. Los límites de octava se pueden calcular utilizando la pendiente mde la línea desde el centro del ojo hasta las coordenadas del mouse. Convenientemente, dividir por cero al calcular la pendiente da infinito en coma flotante (+/-) en lugar de un error.

Ojos visuales en QB64

Sin golf

' Loop forever
DO
    ' Do stuff if there is new mouse data (movement or click)
    IF _MOUSEINPUT THEN
        ' Store the mouse coords rounded to the nearest integer
        mouse_x = CINT(_MOUSEX)
        mouse_y = CINT(_MOUSEY)
        ' If left mouse button was clicked, change location of eyes
        IF _MOUSEBUTTON(1) THEN
            ' Store center coordinates of left eye
            left_center_x = mouse_x - 3
            center_y = mouse_y
        END IF
        ' If eye location is in bounds, print the eyes and pupils
        x_in_bounds = left_center_x > 2 AND left_center_x < 73
        y_in_bounds = center_y > 2 AND center_y < 22
        IF x_in_bounds AND y_in_bounds THEN
            CLS
            FOR eye = 1 TO 2
                ' eye = 1 for left eye, eye = 2 for right eye
                IF eye = 1 THEN center_x = left_center_x
                IF eye = 2 THEN center_x = left_center_x + 6
                ' Print eye borders
                LOCATE center_y - 2, center_x - 2
                PRINT ".---."
                FOR row = 1 TO 3
                    LOCATE , center_x - 2
                    PRINT "|   |"
                NEXT row
                LOCATE , center_x - 2
                PRINT "'---'"
                ' Calculate coordinates of pupil
                xdiff = mouse_x - center_x
                ydiff = mouse_y - center_y
                slope = ydiff / xdiff
                ' For most cases, adding the sign of the diff to the center
                ' coordinate is sufficient
                pupil_x = center_x + SGN(xdiff)
                pupil_y = center_y + SGN(ydiff)
                ' But in octants 3 and 7, the x-coordinate is centered
                IF ABS(slope) > 2 THEN pupil_x = center_x
                ' And in octants 1 and 5, the y-coordinate is centered
                IF ABS(slope) < 0.5 THEN pupil_y = center_y
                LOCATE pupil_y, pupil_x
                PRINT "0"
            NEXT eye
        END IF   ' in bounds
    END IF   ' mouse data
LOOP   ' forever
DLosc
fuente
Han pasado una década o dos desde que usé QB, pero ¿no podrías usarlo en ?0lugar de ?"0"? Esto sugiere que puede usar una expresión numérica y cadenas.
Joey
@Joey Hmm. Imprimirlo como un número también imprime un espacio antes y después ... pero ahora que lo pienso, apuesto a que podría imprimir las pupilas primero y luego eso no sería un problema. Excepto que tendría que imprimir los bordes izquierdo y derecho por separado en lugar de como "| |". Por lo tanto, probablemente no guardaría nada. "0"es solo 2 bytes más largo.
DLosc
7

Código de máquina 6502 ( mouse C64 + 1351 ), 630 bytes

00 C0 20 44 E5 A9 FF 85 5E A2 3F A9 00 8D 10 D0 8D 1B D0 9D C0 02 CA 10 FA A0
0A A2 1E B9 5A C2 9D C0 02 CA CA CA 88 10 F4 A9 0B 8D F8 07 A9 18 8D 00 D0 A9
32 8D 01 D0 A9 0D 8D 27 D0 A9 01 8D 15 D0 78 A9 60 8D 14 03 A9 C1 8D 15 03 58
D0 FE 84 FD 85 FE A8 38 E5 FD 29 7F C9 40 B0 04 4A F0 0A 60 09 C0 C9 FF F0 03
38 6A 60 A9 00 60 20 44 E5 A5 69 38 E9 05 B0 02 A9 00 C9 1E 90 02 A9 1D 85 FD
18 69 02 85 5C 69 06 85 5D A5 6A 38 E9 02 B0 02 A9 00 C9 15 90 02 A9 14 85 FE
18 69 02 85 5E A9 65 8D BB C0 A9 C2 8D BC C0 A9 04 85 02 A6 FE 20 F0 E9 A9 02
85 5F A4 FD A2 00 BD FF FF 91 D1 C8 E8 E0 05 D0 F5 C8 C6 5F D0 EE E6 FE A9 6A
8D BB C0 A9 C2 8D BC C0 C6 02 30 0E D0 D1 A9 6F 8D BB C0 A9 C2 8D BC C0 D0 C5
60 C5 69 90 0A F0 5D E5 69 85 5F A9 C6 D0 09 49 FF 38 65 69 85 5F A9 E6 8D 1C
C1 8D 23 C1 8D 3E C1 A5 6A C5 5E 90 21 F0 12 E5 5E C5 5F 90 12 4A C5 5F B0 02
C6 FD A6 5E E8 D0 33 C6 FD A6 5E D0 2D 0A C5 5F B0 EE 90 F3 49 FF 38 65 5E C5
5F 90 0C 4A C5 5F B0 02 C6 FD A6 5E CA D0 11 0A C5 5F B0 F4 90 D7 A5 6A C5 5E
90 EE F0 D1 B0 C8 20 F0 E9 A9 30 A4 FD 91 D1 60 AD 19 D4 A4 FB 20 4E C0 84 FB
85 5F 18 6D 00 D0 8D 00 D0 6A 45 5F 10 08 A9 01 4D 10 D0 8D 10 D0 AD 10 D0 4A
AD 00 D0 B0 08 C9 18 B0 16 A9 18 D0 0F C9 58 90 0E 24 5F 10 05 CE 10 D0 B0 EF
A9 57 8D 00 D0 AD 1A D4 A4 FC 20 4E C0 84 FC 49 FF 85 5F 38 6D 01 D0 8D 01 D0
6A 45 5F 10 06 24 5F 10 11 30 07 AD 01 D0 C9 32 B0 04 A9 32 D0 06 C9 FA 90 05
A9 F9 8D 01 D0 A5 69 85 6B A5 6A 85 6C AD 10 D0 4A AD 00 D0 6A 38 E9 0C 4A 4A
85 69 AD 01 D0 38 E9 32 4A 4A 4A 85 6A AD 01 DC 29 10 C5 6D F0 0B 85 6D 29 10
D0 05 20 6C C0 30 10 A5 5E 30 46 A5 69 C5 6B D0 06 A5 6A C5 6C F0 3A A6 5E CA
86 5F A9 03 85 02 A6 5F 20 F0 E9 A9 20 A2 03 A4 5C 88 91 D1 C8 CA D0 FA A2 03
A4 5D 88 91 D1 C8 CA D0 FA E6 5F C6 02 D0 DD A5 5C 85 FD 20 E9 C0 A5 5D 85 FD
20 E9 C0 4C 31 EA 80 C0 E0 F0 F8 FC F0 D8 18 0C 0C 2E 2D 2D 2D 2E 5D 20 20 20
5D 27 2D 2D 2D 27

En acción:

manifestación

No hay demostración en línea , lo siento, porque hay un emulador AFAIK no js C64 que admite un mouse. Si quieres probarlo tú mismo, toma VICE , descarga el ejecutable binario e inícialo en el emulador C64:

x64sc -autoload xeyes.prg -controlport1device 3 -keybuf 'sys49152\n'

Para tomar / quitar la entrada del mouse en el emulador en ejecución, úsela ctrl+men Unix / Linux y ctrl+qen Windows.


Sí, esto se tenía que hacer;) Después de todo, no es un ratón Commodore original para el C64, pero por supuesto, el sistema operativo incorporado no lo soporta, por lo que primero necesitaba un controlador de ratón, que ya se llevó a 230 bytes ( incluido un sprite de hardware en forma de cursor del mouse y un código de verificación de límites para el área de la pantalla, pero sin traducir las coordenadas del puntero a las coordenadas de la pantalla de texto).

  • Para proteger algunos bytes, decidí mantener el IRQ del sistema operativo funcionando y usar algunas rutinas de Kernal siempre que sea posible (borrar la pantalla y obtener un puntero base para una fila de pantalla de texto).
  • El código también pone todas las variables en cero páginas, lo que ahorra algunos bytes más, pero destruye los valores de coma flotante utilizados por BASIC. Como el programa nunca sale de todos modos, esto no importa.
  • El tercer truco para reducir el tamaño es la auto modificación: solo hay un código para verificar si se coloca la pupila en el lado izquierdo del ojo. El mismo código se reutiliza después de parchear algunas instrucciones de disminución para incrementar las instrucciones para el lado derecho.

Si está interesado, puede leer el código como fuente de ensamblaje aquí :)

Felix Palmen
fuente
Parece que soy el único que intenta competir aquí de vez en cuando con el código C64. Me encantó este desafío, porque un mouse en el C64 es algo "exótico". Si alguien se pregunta por qué estoy menos activo últimamente, esta es la razón: csdb.dk/release/?id=161435 - finalmente tratando de hacer un juego con todas las funciones para el C64 :)
Felix Palmen
1
Solo por diversión, hice una "versión de lujo": csdb.dk/release/?id=161762
Felix Palmen
7

Limpio , 1014 904 892 884 840 814 782 772 769 bytes

-6 bytes si los ojos no necesitan ajustarse a una cuadrícula

Esto no fue fácil. Las IU en lenguajes funcionales rara vez lo son.

import StdEnv,StdIO,osfont,ostoolbox
a=toReal
c=1>0
Start w#(d,w)=openId w
#(t,w)=worldGetToolbox w
#(_,f,_)=osSelectfont("Courier",[],9)t
=let$p#(s,p)=accPIO getProcessWindowSize p
    =snd(openWindow NilLS(Window""NilLS[WindowId d,WindowMouse(\_=c)Able(noLS1@),WindowViewSize s,WindowPen[PenFont f]])p);@(MouseUp p _)s={s&ls=p};@(MouseMove p _)s=:{ls={x,y},io}={s&io=setWindowLook d c(c,(\_{newFrame}i#(w,i)=getFontCharWidth f' '(unfill newFrame i)
    =let g v=let m=y-p.y;n=p.x-x-v*w;s=abs(a m/a n);k|abs m<9&&abs n<w=5|s<0.4142=if(n>0)6 4=sign if(s>2.4143)0n+if(m>0)2 8in[".---.":["|"+++{if(k==e)'0'' '\\e<-[j..j+2]}+++"|"\\j<-[1,4,7]]]++["'---'"]in foldr(\e=drawAt{x=(x/w-5)*w,y=(y/9+e-2)*9}([a+++" "+++b\\a<-g -3&b<-g 3]!!e))i[0..4]))io};@_ s=s
in startIO SDI zero$[]w

Asegúrese de estar usando iTasks Clean, tener la Courierfuente instalada y StdLibANTES de cualquier subcarpeta ObjectIOen la ruta de búsqueda del módulo.

Compilar con (ejemplo, puede diferir): clm -IL StdLib -IL ObjectIO -IL "ObjectIO/OS <YOUR_OS_HERE>" -IL Dynamics -IL Generics -IL Platform -nci <MODULE_NAME_HERE>

Si nunca antes ha ejecutado Clean, espere que este proyecto tarde más de 5 minutos en compilarse.

Sin golf:

module main
import StdEnv,StdIO,osfont,ostoolbox
height=9
SlopeFor225 :== 0.4142

StartSize :== 8

Universe :== {corner1={x=0,y=0},corner2={x=1,y=1}}

Start :: *World -> *World
Start world = startConsole (openIds 1 world)

startConsole :: ([Id],*World) -> *World
startConsole ([windowID],world)
    # (toolbox,world) = worldGetToolbox world
    # (_,font,toolbox) = osSelectfont ("Consolas",[],height) toolbox
    = startIO SDI {x=0,y=0} (initialise font) [ProcessClose closeProcess] world
where
    initialise font pst
        # (size,pst) = accPIO getProcessWindowSize pst
        # (error,pst) = openWindow undef (window font size) pst
        | error<>NoError = abort "bad window"
        = pst

    window font size
        = Window "Xeyes" NilLS
            [WindowId           windowID
            ,WindowClose        (noLS closeProcess)
            ,WindowMouse        mouseFilter Able (noLS1 track)
            ,WindowViewDomain   Universe//(getViewDomain StartSize)
            ,WindowViewSize     size
            ,WindowPen          [PenFont font]
            ]

    track (MouseDown pos _ _) state=:{ls=point=:{x,y},io}
        # point = pos
        // move to mouse position
        = {state & ls=pos}

    track (MouseMove pos _) state=:{ls=point=:{x,y},io}
        //redraw to point at mouse
        # io = setWindowLook windowID True (True, look) io
        = {state & ls=point,io=io}
    where
        look _ {newFrame} picture
            # picture = unfill newFrame picture
            # (width,picture) = getPenFontCharWidth' 'picture
            = let
                determineSector u
                    # yDist = (y - pos.y)
                    # xDist = (pos.x - u)
                    # slope = abs(toReal yDist / toReal xDist)
                    | (abs yDist) < height && (abs xDist) < width = '9'
                    | slope < SlopeFor225 = if(xDist > 0) '1' '5'
                    | yDist > 0
                        | slope > (2.0+SlopeFor225) = '7'
                        = if(xDist > 0) '8' '6'
                    | slope > (2.0+SlopeFor225) = '3'
                    = if(xDist > 0) '2' '4'
                getEye u=map(map(\e|isDigit e=if(e==determineSector(x+u*width))'0'' '=e))[['.---.'],['|678|'],['|591|'],['|432|'],['\'---\'']]
            in foldr(\i pic=drawAt{x=(x/width-5)*width,y=(y/height+i-2)*height}([toString(a++[' ':b])\\a<-getEye -3&b<-getEye 3]!!i)pic)picture[0..4]

    mouseFilter (MouseDown _ _ _) = True
    mouseFilter (MouseMove _ _) = True
    mouseFilter _ = False

Como puede ver en la versión no adaptada, la mayor parte del código está configurando la combinación de "fuente monoespaciada" con "responder al mouse". Y aunque Courierno lo hace fácil de decir, en realidad está dibujando los .sy 's. Cambiar a algo así lo Consolashace más claro.

ingrese la descripción de la imagen aquí

Οurous
fuente
1
No sé limpio en todo, así que quizás estoy diciendo algo raro, pero es posible cambiar (abs m)<9&&(abs n)<w='9'a (abs m)<9&(abs n)<w='9'? Además, sugiero agregar una pantalla a gif en lugar de una captura de pantalla.
Kevin Cruijssen
1
@KevinCruijssen Eso no funcionaría por varias razones, pero ahorré 4 bytes dejando los corchetes en la misma expresión, ¡así que gracias! ¡También agregué un gif de pantalla!
Οurous
1

Rubí, 335 + 13 = 348 bytes

+13 bytes para que la -rio/consolebandera habilite IO#getch.

Contiene 0x1bcaracteres ESC ( ) literales , como se muestra a continuación. xxd dump sigue.

Precaución: Esto no se limpia después de sí mismo en la salida. Vea la nota debajo del volcado xxd a continuación.

include Math
$><<"␛[?1003h"
s=""
(s<<STDIN.getch
($><<"␛[2J"
x,y=$3.ord-32,$4.ord-32
u,v=x,y if$2
u&&[x-u+3,x-u-3].map{|a|b=y-v
e=4*asin(b/sqrt(a**2+b**2))/PI
printf"␛[%d;%dH.---.@|567|@|480|@|321|@'---'".gsub(/(#{(a<0?4-e:b<0?8+e:e).round%8rescue 8})|([0-8])|@/){$1?0:$2?" ":"␛[5D␛[1B"},v-2,x-a-2}
s="")if /M(C|(#))(.)(.)$/=~s)while 1

Sin golf

Este es un golf bastante ingenuo de mi implementación original de Ruby .

include Math       # Saves a few bytes for asin, sqrt, and PI
$> << "␛[?1003h"   # Print xterm control sequence to start mouse tracking
s = ""             # Variable to hold input-so-far
(
  s << STDIN.getch   # Read a character from STDIN
  (
    $> << "␛[2J"                     # Clear terminal
    x, y = $3.ord - 32, $4.ord - 32  # Get cursor x and y from last match
    u, v = x, y if $2                # Update eye position if last matched control sequence was click ("#")

    u && [x-u+3, x-u-3].map {|a|     # For each eye's x-position
      b = y - v                                       # Eye's y position
      e = 4 * asin(b / sqrt(a**2 + b**2)) / PI        # Convert cursor (x,y) to angle w/ x-axis as 1/8 turns

      printf "␛[%d;%dH.---.@|567|@|480|@|321|@'---'"  # Control code to move text cursor, followed by template for eye
        .gsub(
          /(#{
            (a < 0 ? 4-e : b < 0 ? 8+e : e).round % 8 rescue 8  # Octant number 0-7 or 8 for center
          })|([0-8])|@/
        ){ $1 ? 0 : $2 ? " " : "␛[5D␛[1B" },            # Replace octant number with pupil; other digits with space; and @s with code to move cursor left and down for next line of eye
        v-2, x-a-2                                      # (y, x) position of top left corner of eye
    }
    s = ""                           # Clear input-so-far
  ) if /M(C|(#))(.)(.)$/ =~ s      # ...when input-so-far matches a movement ("C") or click ("#") control sequence
) while 1                        # ...forever

volcado xxd

Este programa activa el seguimiento del mouse con la secuencia de control xterm \e[?1003hpero no lo desactiva al salir. Para apagarlo, use la secuencia de control \e[?1003l, por ejemplo:

ruby -rio/console visual_eyes.rb; printf '\e[1003l'

Como el programa se come todas las entradas, es difícil salir. Si desea salir presionando Ctrl + C, agregue la siguiente línea a continuación (s<<STDIN.getch:

exit 130 if s.end_with?(?\003)

Sin más preámbulos:

00000000: 696e 636c 7564 6520 4d61 7468 0a24 3e3c  include Math.$><
00000010: 3c22 1b5b 3f31 3030 3368 220a 733d 2222  <".[?1003h".s=""
00000020: 0a28 733c 3c53 5444 494e 2e67 6574 6368  .(s<<STDIN.getch
00000030: 0a28 243e 3c3c 221b 5b32 4a22 0a78 2c79  .($><<".[2J".x,y
00000040: 3d24 332e 6f72 642d 3332 2c24 342e 6f72  =$3.ord-32,$4.or
00000050: 642d 3332 0a75 2c76 3d78 2c79 2069 6624  d-32.u,v=x,y if$
00000060: 320a 7526 265b 782d 752b 332c 782d 752d  2.u&&[x-u+3,x-u-
00000070: 335d 2e6d 6170 7b7c 617c 623d 792d 760a  3].map{|a|b=y-v.
00000080: 653d 342a 6173 696e 2862 2f73 7172 7428  e=4*asin(b/sqrt(
00000090: 612a 2a32 2b62 2a2a 3229 292f 5049 0a70  a**2+b**2))/PI.p
000000a0: 7269 6e74 6622 1b5b 2564 3b25 6448 2e2d  rintf".[%d;%dH.-
000000b0: 2d2d 2e40 7c35 3637 7c40 7c34 3830 7c40  --.@|567|@|480|@
000000c0: 7c33 3231 7c40 272d 2d2d 2722 2e67 7375  |321|@'---'".gsu
000000d0: 6228 2f28 237b 2861 3c30 3f34 2d65 3a62  b(/(#{(a<0?4-e:b
000000e0: 3c30 3f38 2b65 3a65 292e 726f 756e 6425  <0?8+e:e).round%
000000f0: 3872 6573 6375 6520 387d 297c 285b 302d  8rescue 8})|([0-
00000100: 385d 297c 402f 297b 2431 3f30 3a24 323f  8])|@/){$1?0:$2?
00000110: 2220 223a 221b 5b35 441b 5b31 4222 7d2c  " ":".[5D.[1B"},
00000120: 762d 322c 782d 612d 327d 0a73 3d22 2229  v-2,x-a-2}.s="")
00000130: 6966 202f 4d28 437c 2823 2929 282e 2928  if /M(C|(#))(.)(
00000140: 2e29 242f 3d7e 7329 7768 696c 6520 31    .)$/=~s)while 1
Jordán
fuente