Visualize olhos visuais


42

Você pode ou não se lembrar do Xeyes, um programa de demonstração que acompanha (e, até onde eu sei, ainda acompanha) o sistema X window. Seu objetivo era desenhar um par de olhos que seguiam o cursor do mouse:

Xeyes

Seu desafio é recriar Xeyes com arte ASCII. Escreva um programa ou função que desenhe dois olhos artísticos ASCII (especificados abaixo) onde o usuário clica e depois move as pupilas para apontar na direção do cursor.

Terminal Eyes GIF

O GIF acima é uma gravação dessa implementação Ruby sem golf , que pode ser executada com qualquer versão recente do Ruby. Você também pode achar útil como referência para seqüências de controle do Xterm.

Especificações

Isso é , então a solução com o menor número de bytes vence.

Este é um desafio, assim que seu programa deve desenhar usando caracteres ASCII, especificamente, os personagens -, ., |, ', 0, espaço, e de nova linha. 1 2

Este é um desafio , portanto, seu programa deve aceitar entradas e extrair suas saídas em tempo real. 3

Antes de seu programa começar a aceitar entrada, ele deve inicializar uma tela em branco de pelo menos 20 linhas e 20 colunas. Não deve desenhar nada até o usuário clicar na tela.

Sempre que o usuário clicar em 4 na tela, o programa deverá limpar qualquer saída anterior e, em seguida, desenhar esses olhos ASCII na tela, centralizados no caractere mais próximo da localização do cursor do mouse. 5 6 (Abaixo, representa o cursor do mouse e não deve ser desenhado.)

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

Observe como as pupilas "apontam" em direção ao cursor.

Sempre que o cursor do mouse se move sobre a tela, o programa deve redesenhar os alunos para que continuem apontando em direção ao cursor, 7 por exemplo:

             ✧


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

Aluno apontando

Suponha que enumeremos as posições dos nove caracteres internos de cada olho da seguinte maneira:

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

O aluno será sorteado em um dos locais 1- 9. Para decidir qual, finja que os caracteres são quadrados e que a tela é uma grade cartesiana, com o centro do 9caractere em (0, 0), o centro de 1em (1, 0) e assim por diante. Quando o programa recebe entrada - um clique ou movimento - ele deve mapear o local de entrada para a coordenada da grade mais próxima 𝑀. Se 𝑀 for (0, 0), o aluno deve ser desenhado em (0, 0), ou seja, o local 9acima. Caso contrário, ele deve ser desenhado conforme descrito abaixo.

Imagine um plano cartesiano sobreposta no grid e dividido em oitantes numerada 1 - 8 :

Se 𝑀 estiver dentro de 1 de outubro , então a pupila deve ser desenhada no local 1acima, ou seja, em (1, 0). Se 𝑀 estiver em 2 de outubro, ele deve ser desenhado em 2- e assim por diante. Para ilustrar, a imagem abaixo mostra parte da grade codificada por cores de acordo com o local em que a pupila deve ser desenhada quando o cursor do mouse estiver em um local específico. Quando, por exemplo, o cursor estiver em qualquer uma das coordenadas verdes (lembrando que as coordenadas da grade estão no centro dos quadrados, não nos cantos), o aluno deve ser desenhado 4.

As pupilas dos dois olhos se movem independentemente, portanto, para cada olho, repita o processo com 𝑀 em relação ao centro desse olho.

Notas

  1. Este não é um desafio de . A saída deve ser uma grade de caracteres. Obviamente, você pode usar rotinas gráficas para desenhar uma grade de caracteres.

  2. O espaço em branco pode ser desenhado (ou melhor, não desenhado), no entanto, é conveniente. Um ponto vazio na grade é igual a um caractere de espaço e será considerado equivalente.

  3. "Tempo real" é definido aqui como menos de 200ms entre a entrada e a saída correspondente que está sendo desenhada.

  4. É a seu critério quais botões do mouse são observados para entrada e se uma pressão ou liberação constitui um "clique".

  5. A tela deve ser limpa ou o equivalente visual deve ser alcançado. Com uma solução baseada em terminal, por exemplo, imprimir uma nova tela abaixo da tela anterior não é considerado equivalente.

  6. Quando o usuário clica perto da borda da tela, de modo que alguns dos caracteres oculares sejam desenhados além da borda, o comportamento é indefinido. No entanto, o programa deve continuar sendo executado normalmente nos cliques subsequentes.

  7. Quando o cursor do mouse sai da "tela", o comportamento é indefinido, mas o programa deve continuar sendo executado normalmente quando o cursor entra novamente na tela.

  8. Um cursor de texto pode aparecer na tela, desde que não oculte a saída.

As brechas padrão são proibidas.


2
@ Ousurous Como quantos minutos são "alguns", nesse caso, dependeriam da quantidade de memória que o sistema possui, e isso poderia nos levar a "esta solução assume que o ambiente possui 512 GB de RAM", eu vou dizer que ele deve ser executado indefinidamente.
Jordan

1
@TaylorScott Nope. Veja a nota 6 (a menos que eu tenha entendido mal sua pergunta).
Jordan

1
@ Οurous Sim, e não. Se o seu ambiente de destino geralmente é aquele em que a fonte padrão é monoespaçada (por exemplo, um emulador de terminal ou editor de código), tudo bem. Se o uso de uma fonte monoespaçada nesse ambiente geralmente requer configuração extra (como em uma solução JS baseada em navegador), essa configuração deve fazer parte da sua contagem de bytes (por exemplo, <pre>ou font-family:monospace).
Jordânia

9
+1 para grande título (ou título ruim, dependendo de como você levá-la)
FantaC

1
@ Ousurous Não, desde que não termine inesperadamente.
Jordan

Respostas:


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> |
|   | |   |
'---' '---'


Ambos X||Ypodem ser jogados X|Ypara economizar 2 bytes.
Kevin Cruijssen 17/01/19

Não funciona tão bem quando você clica perto da parte inferior do contêiner e precisa rolar para baixo. i.stack.imgur.com/s44KU.png Não tenho certeza se específica para o wrapper trecho, mas valia a pena mencionar.
Draco18s

2
@ Οurous É ambiguamente redigido: "centrado na localização do cursor do mouse". "Location" significa "cell grid" ou pode significar "pixel"? Concordo que a intenção era provavelmente a primeira, mas a redação certamente parece permitir a segunda.
DLosc

@KevinCruijssen Infelizmente, isso não funciona - |acaba tendo precedência sobre a expressão ternária.
precisa saber é o seguinte

@darrylyeo Não, não? : S Esta tabela de precedência do operador JavaScript mostra |e ||de certa forma no mesmo nível, e ambos acima ?:. Ambos X||Y?w*r(cos(a)):0e X||Y?h*r(sin(a)):0estão atualmente no formulário boolean_condition?A:B. Portanto, quando você mudar X||Ypara X|Yele, fará um OR bit a bit e depois interpretará como uma condição booleana novamente. ( (X||Y)?A:Bvs (X|Y)?A:B, não X|(Y?A:B)). Além disso, não vejo diferença quando uso "Copiar snippet para responder" e altero ||para |. Tudo ainda funciona exatamente da mesma, tanto quanto eu posso dizer ..
Kevin Cruijssen

12

Excel VBA, 630 bytes

Sub-rotina de planilha declarada que é executada com o clique do mouse, sem nenhuma entrada e produz um par de olhos que seguem o cursor. Isso depende da função auxiliar incluída e da declaração de tipo, que deve ser colocada em um módulo normal.

Esta versão é calibrada para executar com o zoom padrão de 100%. Quebra se você tentar rolar.

Nota: O VBA preenche automaticamente a sequência não terminada na nova linha; portanto, no código abaixo, há três casos em que um terminal "foi incluído apenas para fins de destaque - eles não contribuem para o número 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

Função auxiliar e declaração de tipo

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

Sem Golfe e Comentado

Esta versão é calibrada para ser executada com um nível de zoom de 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

Função auxiliar e declaração 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

Saída

Gif

Moving_Eyes

Imagem de maior resolução

Static_Eyes


Isso não corresponde à especificação de algumas maneiras. 1. “Grade de caracteres” significa caracteres únicos com posições distintas. Quando o cursor do mouse estiver ativado, digamos, o 'caractere mais à direita , a saída será diferente de quando estiver no 'caractere mais à esquerda . 2. A posição dos olhos não é fixa. Um clique do mouse deve fazer com que eles se movam para a posição clicada. Sou flexível no método de entrada (eu aceitaria, digamos, um cursor virtual do mouse controlado pelas teclas de seta), mas há dois eventos de entrada distintos com comportamento distinto: movimento do mouse e clique do mouse.
Jordan

@ Jordan Eu não tenho muita certeza do que você quer dizer com o ponto 1, você poderia elaborar? Quanto ao ponto 2, os olhos não são estáticos, e clicar em qualquer célula da planilha em que a sub-rotina é colocada deve acionar o Worksheet_SelectionChangeevento e passar no intervalo de chamada ( Targetou Tneste caso) - que redesenha os olhos e um *na chamada célula
Taylor Scott

1
@Jordan - Acredito que já resolvi todas e todas as suas preocupações, embora, ao fazê-lo, precisei limitar minha solução ao Excel de 64 bits e estou trabalhando em uma versão não-gasta e comentada neste momento
Taylor Scott

1
@ Jordan Isso ocorre porque as declarações da API do Windows para 32 e 64, mas o VBA são diferentes, assim como as especificações de concatenação e exponenciação, onde 32 bits é quase sempre mais curto - e atualmente não tenho acesso a uma versão de 32 bits do Office: P
Taylor Scott

3
Talvez altere as duas capturas de tela para uma tela para 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

Clique esquerdo coloca os olhos. Se a colocação dos olhos resultar em parte fora dos limites, o programa "congela" até que uma colocação válida seja feita.

A principal parte complicada é colocar os alunos. Na maioria das vezes, as coordenadas da pupila são apenas o centro do olho mais (sinal (Δx), sinal (Δy)), exceto que nos octantes 1 e 5, a coordenada y é igual ao centro y e nos octantes 3 e 7, a coordenada x é igual ao centro x. Os limites dos octantes podem ser calculados usando a inclinação mda linha do centro do olho até as coordenadas do mouse. Convenientemente, dividir por zero ao calcular a inclinação gera infinito de ponto flutuante (+/-) em vez de um erro.

Olhos visuais em QB64

Ungolfed

' 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

Faz uma década ou duas desde que usei o QB, mas você não poderia usar em ?0vez de ?"0"? Isso sugere que você pode usar uma expressão numérica e seqüências de caracteres.
Joey

@Joey Hmm. Imprimir em número também imprime um espaço antes e depois ... mas, pensando bem, aposto que poderia imprimir primeiro os alunos e depois isso não seria um problema. Exceto que eu teria que imprimir as bordas esquerda e direita separadamente em vez de como "| |". Portanto, provavelmente não salvaria nada. "0"tem apenas 2 bytes a mais.
DLosc

7

Código da 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

Em ação:

demonstração

Não há demonstração on-line , desculpe, porque não há emulador AFAIK sem js C64 suportando um mouse. Se você quiser experimentar, pegue o VICE , baixe o executável binário e inicie-o no emulador C64:

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

Para pegar / desagrupar a entrada do mouse no emulador em execução, use ctrl+mno Unix / Linux e ctrl+qno Windows.


Sim, isso tinha que ser feito;) Afinal, não é um rato Commodore original para o C64, mas é claro, o sistema operacional embutido não apoiá-lo, então eu precisava primeiro um driver de mouse, que já levou 230 bytes ( incluindo um sprite de hardware em forma de cursor do mouse e limites de verificação de código para a área da tela, mas sem converter as coordenadas do ponteiro em coordenadas da tela de texto).

  • Para proteger alguns bytes, decidi manter o IRQ do sistema operacional funcionando e usar algumas rotinas Kernal sempre que possível (limpar a tela e obter um ponteiro base para uma linha da tela de texto).
  • O código também coloca todas as variáveis ​​no zeropage, o que economiza mais alguns bytes, mas destrói os valores de ponto flutuante usados ​​pelo BASIC. Como o programa nunca sai de qualquer maneira, isso não importa.
  • O terceiro truque para reduzir o tamanho é a auto-modificação: existe apenas um código a ser verificado para colocar a pupila no lado esquerdo do olho. O mesmo código é reutilizado após o patch de algumas instruções de decremento para incrementar as instruções para o lado direito.

Se você estiver interessado, pode ler o código como fonte de montagem aqui :)


Eu pareço ser o único tentando competir aqui de tempos em tempos com o código C64. Adorei esse desafio, porque um mouse no C64 é algo "exótico"! Se alguém perguntar por que eu estou menos ativo ultimamente, esta é a razão: csdb.dk/release/?id=161435 - finalmente tentando fazer um jogo full-featured para o C64 :)
Felix Palmen

1
Apenas para o divertimento dele, eu fiz uma versão "deluxe": csdb.dk/release/?id=161762
Felix Palmen

7

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

-6 bytes se os olhos não precisarem se encaixar em uma grade

Isso não foi fácil. UIs em idiomas funcionais raramente são.

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

Certifique-se de usar o iTasks Clean, ter a Courierfonte instalada e StdLibANTES de qualquer subpasta ObjectIOno caminho de pesquisa do módulo.

Compile com (por exemplo, pode ser diferente): clm -IL StdLib -IL ObjectIO -IL "ObjectIO/OS <YOUR_OS_HERE>" -IL Dynamics -IL Generics -IL Platform -nci <MODULE_NAME_HERE>

Se você nunca executou o Clean antes, espere que este projeto leve mais de 5 minutos para ser compilado.

Ungolfed:

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 você pode ver na versão simplificada, a maior parte do código está apenas configurando a combinação de "fonte monoespaçada" com "responder ao mouse". E mesmo que Couriernão torná-lo fácil de dizer, é realmente desenhar as .s e 's. Trocar para algo assim Consolastorna mais claro.

insira a descrição da imagem aqui


1
Eu não conheço o Clean, então talvez eu esteja dizendo algo estranho, mas é possível mudar (abs m)<9&&(abs n)<w='9'para (abs m)<9&(abs n)<w='9'? Além disso, sugiro adicionar uma tela para gif em vez de captura de tela.
Kevin Cruijssen

1
@KevinCruijssen Isso não funcionaria por várias razões, mas eu salvei 4 bytes soltando os colchetes na mesma expressão, então obrigado! Eu também adicionei um gif na tela!
Οurous

1

Ruby, 335 + 13 = 348 bytes

+13 bytes para -rio/consoleativar o sinalizador IO#getch.

Contém caracteres literais ESC ( 0x1b), mostrados abaixo. O despejo xxd segue.

Cuidado: Isso não se limpa na saída. Veja a nota em xxd dump abaixo.

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

Ungolfed

Este é um campo bastante ingênuo da minha implementação original do 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

despejo xxd

Este programa ativa o rastreamento do mouse com a sequência de controle xterm, \e[?1003hmas não o desativa na saída. Para desligá-lo, use a sequência de controle \e[?1003l, por exemplo:

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

Como o programa consome toda a entrada, é difícil sair. Se você deseja sair pressionando Ctrl + C, adicione a seguinte linha abaixo (s<<STDIN.getch:

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

Sem mais delongas:

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.