Воссоздайте заставку Windows ME как ASCII


19

Эта проблема вдохновлена этим ответом на бирже Ask Ubuntu Stack Exchange.

вступление

Помните заставку Windows ME с трубами ? Время вернуть ностальгию!

введите описание изображения здесь

Вызов

Вы должны написать программу или функцию, которая выведет ASCII-представление заставки. В скринсейвере должна быть одна труба, которая будет расти в полуслучайных направлениях.
Начало трубы будет случайным образом размещено на любой из границ экрана, и часть трубы должна быть перпендикулярна границе (угловые первые трубы могут быть горизонтальными или вертикальными). Каждый тик труба будет расти в том направлении, в котором она находится (горизонтальный / вертикальный) при 80%случайном повороте или при повороте 20%.

Представление трубы

Для создания канала будут использованы 6 символов Юникода

─    \u2500    horizontal pipe
│    \u2502    vertical pipe
┌    \u250C    upper left corner pipe
┐    \u2510    upper right corner pipe
└    \u2514    lower left corner pipe
┘    \u2518    lower right corner pipe

вход

Программа / функция будет принимать 3 значения ввода, которые могут быть собраны через параметры функции или предложены пользователю.

  • Количество тиков
  • Ширина экрана
  • Высота экрана

Количество тиков

Для каждого тика, кусок трубы будет добавлен на экран. Трубы перезапишут старые части трубы, если они появляются в одной и той же позиции.

Например, возьмем экран размером 3х3

ticks == 3
─┐ 
 ┘ 


ticks == 4
─┐ 
└┘ 


ticks == 5
│┐ 
└┘ 

Всякий раз, когда труба выходит из экрана, как в последнем примере с 5 тиками, новая труба будет появляться на случайной границе. Например:

ticks == 6
│┐ 
└┘ 
  ─

Новая труба должна иметь 50% шанс быть горизонтальной или вертикальной.

Ширина экрана / высота

Ширина и высота экрана могут быть объединены в одно значение, если это предпочтительнее для вашего языка. Ширина и высота экрана всегда будут иметь минимальное значение 1 и максимальное значение 255. Если выбранный вами язык поддерживает консоль или экран вывода, размер которого меньше сетки символов 255x255, то вы можете предположить, что ширина и высота будут никогда не выходите за пределы вашей консоли. (Пример: окно Windows 80x25 cmd)

Выход

Вывод вашей программы / функции должен быть выведен на экран или возвращен из функции. Для каждого запуска вашей программы должен генерироваться другой набор каналов.

Контрольные примеры

Следующие тесты являются случайными примерами правильных выходных данных.

f(4, 3, 3)
 │
─┘
  │

f(5, 3, 3)
 │
─┘┌
  │

f(6, 3, 3)
─│
─┘┌
  │

f(7, 3, 3)
──
─┘┌
  │

Очевидно, что чем больше тиков, тем труднее становится доказать правильность вашей программы. Следовательно, публикация GIF вашей работы будет предпочтительнее. Если это невозможно, пожалуйста, опубликуйте версию своего кода, которая включает распечатку вывода. Очевидно, это не будет учитываться при подсчете очков.

правила

  • Это , выигрывает короткое количество байтов
  • Применяются стандартные лазейки
  • Если в исходном коде вы используете символы канала Юникод, вы можете считать их одним байтом

Это довольно сложная задача, которую, возможно, можно решить многими творческими способами. Вам предлагается написать ответ на более многословном языке, даже если ответы уже есть в коротких esolangs. Это создаст каталог кратчайших ответов для каждого языка. Бонус за голосование за модные цветные гифки;)

Удачного игры в гольф!

Отказ от ответственности: я знаю, что символы Unicode не ASCII, но из-за отсутствия лучшего названия я просто называю это ASCII art. Предложения приветствуются :)


9
Символы Юникода, которые вы хотите в выводе, не являются ASCII.
Пшеничный волшебник

2
Я думаю, что это должно быть помечено ascii-artвместо graphical-output- ссылка
AdmBorkBork

13
Ностальгия и Windows ME плохо вписываются в одну линию
Луис Мендо

1
Заставка 3D Pipes появилась в Windows ME.
Нил

1
@ Джордан Я думал, он имел в виду кортежи.
КарлКастор

Ответы:


9

JavaScript (ES6), 264 266 274 281

(t,w,h,r=n=>Math.random()*n|0,g=[...Array(h)].map(x=>Array(w).fill` `))=>((y=>{for(x=y;t--;d&1?y+=d-2:x+=d-1)x<w&y<h&&~x*~y?0:(d=r(4))&1?x=r(w,y=d&2?0:h-1):y=r(h,x=d?0:w-1),e=d,d=r(5)?d:2*r(2)-~d&3,g[y][x]="─└ ┌┐│┌  ┘─┐┘ └│"[e*4|d]})(w),g.map(x=>x.join``).join`
`)

Подсчет символов рисования Юникода как 1 байт каждый. (Как указано в ОП)

Меньше гольфа

(t,w,h)=>{
  r=n=>Math.random()*n|0; // integer range random function
  g=[...Array(h)].map(x=>Array(w).fill(' ')); // display grid
  for (x=y=w;t--;)
    x<w & y<h && ~x*~y||( // if passed boundary
      d = r(4), // select random direction
      d & 1? (x=r(w), y=d&2?0:h-1) : (y=r(h), x=d?0:w-1) // choose start position 
    ),
    e=d, d=r(5)?d:2*r(2)-~d&3, // change direction 20% of times
    g[y][x]="─└ ┌┐│┌  ┘─┐┘ └│"[e*4|d], // use char based on current+prev direction
    d&1 ? y+=d-2 : x+=d-1 // change x,y position based on direction
  return g.map(x=>x.join``).join`\n`
}

Анимированный тест

Примечание: пытаясь сохранить время анимации менее 30 секунд, большее количество тиков ускоряет анимацию

f=(t,w,h,r=n=>Math.random()*n|0,g=[...Array(h)].map(x=>Array(w).fill` `))=>
{
  z=[]
  for(x=y=w;t--;d&1?y+=d-2:x+=d-1)
    x<w&y<h&&~x*~y?0:(d=r(4))&1?x=r(w,y=d&2?0:h-1):y=r(h,x=d?0:w-1),
    e=d,d=r(5)?d:2*r(2)-~d&3,g[y][x]="─└ ┌┐│┌  ┘─┐┘ └│"[e*4|d],
    z.push(g.map(x=>x.join``).join`\n`)
  return z
}

function go() {
  B.disabled=true
  var [t,w,h]=I.value.match(/\d+/g)
  var r=f(+t,+w,+h)
  O.style.width = w+'ch';
  var step=0
  var animate =_=>{
    S.textContent = step
    var frame= r[step++]
    if (frame) O.textContent = frame,setTimeout(animate, 30000/t);
    else   B.disabled=false
  }
  
  animate()
}

go()
#O { border: 1px solid #000 }
Input - ticks,width,height
<input value='600,70,10' id=I><button id=B onclick='go()'>GO</button>
<span id=S></span>
<pre id=O></pre>


Как раз тогда, когда я думал, что QBasic действительно может выиграть в гольф ;) Имейте upvote.
DLosc

12

Ничто так не говорит, ностальгия совсем как ...

QBasic, 332 байта

INPUT t,w,h
RANDOMIZE
CLS
1b=INT(RND*4)
d=b
IF b MOD 2THEN c=(b-1)/2*(w-1)+1:r=1+INT(RND*h)ELSE c=1+INT(RND*w):r=b/2*(h-1)+1
WHILE t
LOCATE r,c
m=(b+d)MOD 4
IF b=d THEN x=8.5*m ELSE x=13*m+(1<((b MOD m*3)+m)MOD 5)
?CHR$(179+x);
r=r-(d-1)MOD 2
c=c-(d-2)MOD 2
b=d
d=(4+d+INT(RND*1.25-.125))MOD 4
t=t-1
IF(r<=h)*(c<=w)*r*c=0GOTO 1
WEND

QBasic - правильный язык для этой задачи, потому что:

  • Его кодировка включает в себя символы рисования блоков - нет необходимости в Unicode
  • LOCATE позволяет печатать в любое место на экране, перезаписывая то, что было ранее
  • Microsoft ®

конкретика

Это гольф QBasic, написанный и протестированный на QB64 с отключенным автоформатированием. Если вы введете / вставите его в реальную среду разработки QBasic, он добавит несколько пробелов и расширится ?доPRINT , но он должен работать точно так же.

Программа вводит три значения, разделенных запятыми: тики, ширина и высота. Затем он запрашивает случайное число семян. (Если это поведение неприемлемо, измените вторую строку наRANDOMIZE TIMER на +6 байт.) Наконец, он рисует трубы на экран.

Максимальные размеры, которые можно ввести: 80 (ширина) на 25 (высота). Задание высоты 25 приведет к обрезанию нижнего ряда, когда QBasic скажет «Нажмите любую клавишу для продолжения».

Как?

TL; DR: много математики.

Текущая строка и столбец являются rи c; текущее направление dи предыдущее направление b. Значения направления 0-3: вниз, вправо, вверх, влево. Арифметика переводит их в правильные значения шага для rиc , а также правильные координаты края , чтобы начать.

Поле для рисования персонажей │┐└─┘┌ - это кодовые точки 179, 191, 192, 196, 217 и 218 в QBasic. Они выглядят довольно случайными, но для генерации чисел с какой-то (довольно запутанной, я не уверен, даже я понимаю) математикой по-прежнему используется меньше символов, чем для набора условных выражений.

Код для изменения направления генерирует случайное число в диапазоне от -0,125 до 1,125 и занимает свое место. Это дает -110% времени, 080% времени и 110% времени. Затем мы добавляем это к текущему значению d, mod 4. Добавление 0 сохраняет текущее направление; добавление +/- 1 делает поворот.

Что касается потока управления, то WHILE t ... WENDэто основной цикл; раздел перед ним, начиная с номера строки 1( 1b=INT(RND*4)), перезапускает канал со случайного ребра. Всякий раз, когда rи cза окном, мы GOTO 1.

Покажите мне GIF!

Ну вот:

Трубы!

Это было сгенерировано несколько отличной версией с анимацией, цветом и автоматическим случайным начальным числом:

INPUT t, w, h
RANDOMIZE TIMER
CLS

restart:
' Calculate an edge to start from

b = INT(RND * 4)
'0: top edge (moving down)
'1: left edge (moving right)
'2: bottom edge (moving up)
'3: right edge (moving left)
d = b

' Calculate column and row for a random point on that edge
IF b MOD 2 THEN
    c = (b - 1) / 2 * (w - 1) + 1
    r = 1 + INT(RND * h)
ELSE
    c = 1 + INT(RND * w)
    r = b / 2 * (h - 1) + 1
END IF
COLOR INT(RND * 15) + 1

WHILE t
    ' Mathemagic to generate the correct box-drawing character
    m = (b + d) MOD 4
    IF b = d THEN
        x = 17 * m / 2
    ELSE
        x = 13 * m + (1 < ((b MOD m * 3) + m) MOD 5)
    END IF
    LOCATE r, c
    PRINT CHR$(179 + x);

    ' Update row and column
    r = r - (d - 1) MOD 2
    c = c - (d - 2) MOD 2
    ' Generate new direction (10% turn one way, 10% turn the other way,
    ' 80% go straight)
    b = d
    d = (4 + d + INT(RND * 1.25 - .125)) MOD 4

    ' Pause
    z = TIMER
    WHILE TIMER < z + 0.01
        IF z > TIMER THEN z = z - 86400
    WEND

    t = t - 1
    IF r > h OR c > w OR r = 0 OR c = 0 THEN GOTO restart
WEND

Я набрал это в моей MS-DOS v6.22 VM :-)
Нил

9

Python 2,7, 624 616 569 548 552 байта

from random import*
from time import*
i=randint
z=lambda a,b:dict(zip(a,b))
c={'u':z('lur',u'┐│┌'),'d':z('ldr',u'┘│└'),'l':z('uld',u'└─┌'),'r':z('urd',u'┘─┐')}
m=z('udlr',[[0,-1],[0,1],[-1,0],[1,0]])
def f(e,t,w,h):
 seed(e);s=[w*[' ',]for _ in' '*h]
 while t>0:
  _=i(0,1);x,y=((i(0,w-1),i(0,1)*(h-1)),(i(0,1)*(w-1),i(0,h-1)))[_];o=('du'[y>0],'rl'[x>0])[_]
  while t>0:
   d=c[o].keys()[i(7,16)//8];s[y][x]=c[o][d];x+=m[d][0];y+=m[d][1];t-=1;sleep(.5);print'\n'.join([''.join(k)for k in s]);o=d
   if(x*y<0)+(x>=w)+(y>=h):break

Первый параметр - это начальное число, одни и те же начальные числа будут генерировать одинаковые выходные данные, печатая каждый шаг с задержкой 500 мс.

  • -10 байт благодаря @TuukkaX

повтори это

Пример запуска

f(5,6,3,3)

будет выводить

   

 ─┐ 
   

──┐ 
   

┘─┐ 
   
┐  
┘─┐ 

подробная версия

import random as r
from time import *
char={
'u':{'u':'│','l':'┐','r':'┌'},
'd':{'d':'│','l':'┘','r':'└'},
'l':{'u':'└','d':'┌','l':'─'},
'r':{'u':'┘','d':'┐','r':'─'}
}
move={'u':[0,-1],'d':[0,1],'l':[-1,0],'r':[1,0]}
def f(seed,steps,w,h):
 r.seed(seed)
 screen=[[' ',]*w for _ in ' '*h]
 while steps > 0:
  if r.randint(0,1):
   x,y=r.randint(0,w-1),r.randint(0,1)*(h-1)
   origin='du'[y>0]  
  else:
   x,y=r.randint(0,1)*(w-1),r.randint(0,h-1)
   origin = 'rl'[x>0]
  while steps > 0:
   direction = char[origin].keys()[r.randint(0,2)]
   screen[y][x]=char[origin][direction]
   x+=move[direction][0]
   y+=move[direction][1]
   steps-=1
   sleep(0.5)
   print '\n'.join([''.join(k) for k in screen]),''
   if x<0 or y<0 or x>=w or y>=h:
    break
   origin=direction

1
Там бесполезные пробелы в if x*y<0 or. 0.5может быть уменьшен до .5.import *может быть import*. ''.join(k) forимеет бесполезные пробелы. Вы также должны иметь возможность хранить dictпеременную и вызывать ее каждый раз, когда вы ее используете. Не проверял, сколько это экономит, но, сохраняя dict(zip(a,b))в лямбде, который выполняет работу для двух строк (a, b), он должен нарезать некоторые. +1.
Yytsi

7

C (GCC / Linux), 402 353 352 302 300 298 296 288 байт

#define R rand()%
x,y,w,h,r;main(c){srand(time(0));scanf(
"%d%d",&w,&h);for(printf("\e[2J");x%~w*
(y%~h)||(c=R 8,(r=R 4)&1?x=1+R w,y=r&2
?1:h:(y=1+R h,x=r&2?1:w));usleep('??'))
printf("\e[%dm\e[%d;%dH\342\224%c\e[H\n",
30+c,y,x,2*"@J_FHAF__L@HL_JA"[r*4|(r^=R 5
?0:1|R 4)]),x+=--r%2,y+=~-r++%2;}

Благодарим edc65 за сохранение направления в одном 4-битном числе.

Считывает ширину / высоту в stdin перед зацикливанием заставки навсегда. Например:

gcc -w golf.c && echo "25 25" | ./a.out

Или для полноэкранной заставки:

gcc -w golf.c && resize | sed 's/[^0-9]*//g' | ./a.out

Для удобства чтения я добавил новые строки. Требуется машина Linux с терминалом, поддерживающим коды ANSI. Имеет цвета! Если удалить поддержку цвета, это будет стоить на 17 байт меньше.

пример


5

Рубин, 413 403 396 байт

Рубиновые трубы

Функция, которая принимает количество тиков и ширину в качестве входных данных и возвращает последний экран в виде строки. Без сомнения, можно играть в гольф больше.

->t,w{k=[-1,0,1,0,-1]
b=(" "*w+$/)*w
f=->t,a=[[0,m=rand(w),2],[w-1,m,0],[m,0,1],[m,w-1,3]].sample{n,m,i=a
d=k[i,2]
q=->n,m,i{_,g,j=rand>0.2?[[1,0],[3,0],[0,1],[2,1]].assoc(i):"021322033132243140251350".chars.map(&:to_i).each_slice(3).select{|c,|c==i}.sample
v,u=k[j||=i,2]
y=n+v
x=m+u
[g,y,x,j]}
g,y,x,j=q[n,m,i]
b[n*w+n+m]="─│┌┐┘└"[g]
y>=0&&y<w&&x>=0&&x<w ?t>1?f[t-1,[y,x,j]]:b:f[t]}
f[t]}

Смотрите его на repl.it: https://repl.it/Db5h/4

Чтобы увидеть его в действии, вставьте следующее после строки, которая начинается b[n*w+n+m]= :

puts b; sleep 0.2

... затем назначьте лямбду переменной, например, pipes=->...и назовите ее как pipes[100,20](для 100 тиков и экрана 20x20).

Ungolfed & объяснение

# Anonymous function
# t - Number of ticks
# w - Screen width
->t,w{
  # The cardinal directions ([y,x] vectors)
  # Up = k[0..1], Right = k[1..2] etc.
  k = [-1, 0, 1, 0, -1]

  # An empty screen as a string
  b = (" " * w + $/) * w

  # Main tick function (recursive)
  # t - The number of ticks remaining
  # a - The current position and vector index; if not given is generated randomly
  f = ->t,a=[[0,m=rand(w),2], [w-1,m,0], [m,0,1], [m,w-1,3]].sample{
    # Current row, column, and vector index
    n, m, i = a
    d = k[i,2] # Get vector by index

    # Function to get the next move based on the previous position (n,m) and direction (d)
    q = ->n,m,i{
      # Choose the next pipe (`g` for glyph) and get the subsequent vector index (j)
      _, g, j = (
        rand > 0.2 ?
          [[1,0], [3,0], [0,1], [2,1]].assoc(i) : # 80% of the time go straight
          "021322033132243140251350".chars.map(&:to_i).each_slice(3)
            .select{|c,|c==i}.sample
      )

      # Next vector (`v` for vertical, `u` for horizontal)
      # If straight, `j` will be nil so previous index `i` is used
      v, u = k[j||=i, 2]

      # Calculate next position
      y = n + v
      x = m + u

      # Return next glyph, position and vector index
      [g, y, x, j]
    }

    # Get next glyph, and subsequent position and vector index
    g, y, x, j = q[n, m, i]

    # Draw the glyph
    b[n * w + n + m] = "─│┌┐┘└"[g]

    # Check for out-of-bounds
    y >= 0 && y < w && x >=0 && x < w ?
      # In bounds; check number of ticks remaining
      t > 1 ?
        f[t-1, [y,x,j]] : # Ticks remain; start next iteration
        b : # No more ticks; return final screen

      # Out of bounds; repeat tick with new random start position
      f[t]
  }
  f[t]
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.