Код Гольф: соединение точек
Возможно, вы помните эти рисунки, когда были ребенком, но теперь пришло время позволить компьютеру рисовать их (в полном великолепии Ascii). Повеселись!
Описание:
Входные данные состоят из нескольких строк (заканчиваются символом новой строки), которые описывают "поле". В этом поле разбросаны "числа" (разделенные пробелами). Все строки можно считать одинаковой длины (вы можете заполнить пробелы до конца).
- числа всегда начинаются с 1
- они следуют за порядком натуральных чисел: каждое "следующее число" увеличивается на 1
- каждое число окружено (по крайней мере) одним пробелом слева и справа
Задача:
Нарисуйте линии между этими числами в их естественном порядке (1 -> 2 -> 3 -> ...N)
(предположим, N <= 99) со следующими характеристиками:
- заменить число на '
+
' персонаж - для горизонтальных линий: используйте '
-
' - для вертикальных линий: используйте '
|
' - идти влево и вниз или вправо и вверх:
/
- идти влево и вверх или вправо и вниз:
\
Важные заметки:
При рисовании линий типа 4 и 5 вы можете предположить (учитывая точки, соединяющиеся с координатами x1, y1 и x2, y2), что
distance(x1,x2) == distance(y1,y2)
, Или другими словами (как прокомментировал пользователь jball): "последовательные элементы, которые не выровнены по горизонтали или вертикали, всегда выровнены по наклонной черте или обратной косой черте".Важно следовать порядку, в котором соединены точки (более новые линии могут выделять более старые линии).
- Пример ввода 1 -
8 7 6 10 9 5 3 4 11 12 13 1 2
- Пример вывода 1 -
+ / | / + - + + -------- + \ / \ / + / | / + - + + | \ | + ------------------------ + + -------------------------- +
- Пример ввода 2 -
64 63 62 61 1 65 66 57 58 2 56 59 45 67 55 46 3 44 54 60 47 53 52 49 48 4 51 50 43 5 42 41 6 23 22 25 26 40 20 21 24 34 7 13 12 33 19 27 32 14 35 8 15 16 39 17 18 28 31 36 9 38 10 11 29 30 37
- Пример вывода 2 - ( ссылка на единорога)
+ / + // // // / + - + + + \ | + + - \ + + \ + \ + / + + \ + \ + \ \ | + | + + + / | + - + + ------- + / + + - + + / \ + + | + + + / \ + \ + --- + + \ + - + + \ / + + + - + / \ / + | / | | + + / + | / + || / // + + + || / // / \ + || / // / \ | || / + / / \ + --- + + + \ + + | | | + | + - + + --- + +
Победитель:
Кратчайшее решение (по количеству символов кода). Ввод можно прочитать через стандартный ввод.
16 ответов
Perl, 222 символа (211)
Perl, 384 365 276 273 253 225 222 218 211 символов (222 после окончания конкурса). Новые строки предназначены только для "читабельности" и не учитываются при подсчете символов.
Последнее редактирование: больше не перезаписывается $"
и печать @S
непосредственно
$_=join'',@S=map{$n=s/$/$"x97/e;(/./g)[0..95],$/}<>;
while(/\b$n /){$S[$q=$-[0]]='+';($P,$Q)=sort{$a-$b}$q,$p||$q;
for(qw'\98 |97 /96 -1'){/\D/;$S[$P]=$&until($Q-$P)%$'||$Q<=($P+=$')}
$n++;$p=$q}s/\d/ /,print for@S
Объяснение:
$_=join'',@S=map{$n=s/$/$"x97/e;(/./g)[0..95],$/}<>;
Эта задача будет проще, если все строки будут одинаковой длины (скажем, 97 символов). Этот оператор берет каждую строку ввода, заменяет символ конца строки на 96 пробелов, затем вставляет первые 96 символов плюс символ новой строки в массив @S
, Обратите внимание, мы также устанавливаем $n=1
, поскольку 1 - это первое число, которое мы будем искать во входных данных. join
оператор создает одну строку из массива @S
, Удобнее использовать скалярную переменную $_
для сопоставления с образцом, и более удобно использовать массив @S
для обновления изображения.
while(/\b$n /){
Поиск номера $n
в переменной $_
, Оценка регулярных выражений в Perl имеет несколько побочных эффектов. Одним из них является установка специальной переменной $-[0]
с положением начала совпавшего узора внутри совпавшей строки. Это дает нам позицию числа $n
в строке $_
а также массив @S
,
Конечно, цикл закончится, когда $n
достаточно высоко, что мы не можем найти его на входе.
$S[$q=$-[0]]='+';
Позволять $q
быть позицией числа $n
в строке $_
и массив @S
и назначьте символ '+' в этой позиции.
$ Р =($ р ||=$ д)+$ д -($Q=$ д> $ р $ д:$ р?)($ Р,$Q)= {сортировки $a-$ B}$ р ||$ д, $ Q;
Первый раз через цикл установите $p
в $q
, После первого раза $p
будет держать предыдущее значение $q
(что будет относиться к позиции на входе предыдущего номера). приписывать $P
а также $Q
такой, что $P
= Мин ($p
,$q
),$Q
= Макс ($p
,$q
)
for(qw'\98 |97 /96 -1'){
По построению, последовательные числа либо
соединены вертикальной линией. Поскольку ввод состоит из 97 символов в каждой строке, это означает, что
$p-$q
делится на 97."выровнен по наклону обратной косой черты", что сделает
$p-$q
делится на 98"выровнен по наклону прямой косой черты", что сделало бы
$p-$q
делится на 96на той же горизонтальной линии
Элементы этого списка кодируют возможное количество позиций между линейными сегментами и символ для кодирования этого сегмента.
/\D/;
Еще одна тривиальная оценка регулярных выражений. Как побочный эффект, он устанавливает специальную переменную $&
(переменная MATCH) к символу сегмента линии (\ | /
или же -
) а также $'
(переменная POSTMATCH) на число (98 97 96 или 1), закодированное в элементе списка.
$S[$P]=$&until($Q-$P)%$'||$Q<=($P+=$')
Это утверждение рисует отрезок между двумя числами. Если $Q-$P
делится на $'
затем продолжайте увеличивать $P
от $'
и назначение персонажа $&
в $S[$P]
до тех пор $P
достигает $Q
, Конкретнее, например, если $Q-$P
делится на 97, то приращение $P
на 97 и установить $S[$P]='|'
, Повторять до $P>=$Q
,
$n++;$p=$q
Подготовьтесь к следующей итерации цикла. инкремент $n
на следующий номер для поиска во входе, и пусть $p
удерживать позицию предыдущего номера.
s/\d/ /,print for@S
Выведите массив, преобразуя любые оставшиеся цифры (из двузначных идентификаторов на входе, где мы перезаписывали только первую цифру с "+") в пробелы по мере продвижения.
Commodore 64 BASIC - 313 символов
РЕДАКТИРОВАТЬ: см. Ниже для версии для гольфа
Небольшое путешествие по переулку памяти с графикой PET, POKE,PEEK и прочим:)
Программа работает непосредственно в памяти экрана, так что вы просто идете вперед, очищаете экран, размещаете точки и набираете RUN:
Вы должны подождать минуту или около того, пока он найдет точки, а затем он начнет рисовать. Это не быстро - вы можете увидеть нарисованные линии, но это самая крутая часть:)
Гольф версия:
Commodore BASIC кажется отличным языком для игры в гольф, потому что он не требует пробелов:) Вы также можете сократить большинство команд, введя первую букву без смещения, за которой следует смещенная вторая буква. Например, POKE
можно набрать как P[SHIFT+O], который выглядит как P┌
на экране:
MS-DOS Batch (да, вы правильно прочитали!)
Я часто слышу (или читаю), что люди говорят, что пакет не очень мощный, и вы не можете сделать с ними много, ну, им я говорю: вот сила BATCH!
Фактический скрипт (script.bat):
set file=%~1
call :FindNextNum 1
for /F "tokens=2 delims=:" %%i IN ('find /c /V "" "%file%"') DO set /a totalLines=%%i
set maxLen=0
for /F "delims=" %%i IN (%file%) DO (
call :CountChars "%%i"
if /i !charCount! gtr !maxLen! set maxLen=!charCount!
)
for /L %%i IN (0,1,%totalLines%) DO set "final_%%i=" & for /L %%j IN (0,1,%maxLen%) DO set "final_%%i=!final_%%i! "
:MainLoop
set currLineNum=%lineNum%
set currCol=%linePos%
set currNum=%nextNum%
set /a targetNum=%currNum%+1
call :FindNextNum %targetNum%
if "%nextNum%"=="" goto MainEnd
REM echo %currNum% -^> %nextNum%
if /I %currLineNum% lss %lineNum% (
call :DrawLine %currCol% %currLineNum% %linePos% %lineNum%
) else (
call :DrawLine %linePos% %lineNum% %currCol% %currLineNum%
)
goto MainLoop
:MainEnd
for /L %%i IN (0,1,%totalLines%) DO echo.!final_%%i!
goto:eof
:DrawLine
if /I %2 equ %4 goto:DrawHoriz
set "char=" & set "pos=%1" & set "inc=0"
if /I %1 LSS %3 set "char=\" & set "pos=%1" & set "inc=1"
if /I %1 GTR %3 set "char=/" & set "pos=%1" & set "inc=-1"
for /L %%i IN (%2,1,%4) DO call :DrawChar %%i !pos! %char% & set /a "pos+=%inc%"
goto:DrawEnds
:DrawHoriz
set "start=%1+1" & set "end=%3"
if /I %start% gtr %end% set "start=%3+1" & set "end=%1"
set /a lineEnd=%end%+1
set lineEnd=!final_%2:~%lineEnd%!
for /L %%i IN (%start%,1,%end%) DO set final_%2=!final_%2:~0,%%i!-
set final_%2=!final_%2!!lineEnd!
:DrawEnds
call :DrawChar %2 %1 +
call :DrawChar %4 %3 +
goto:eof
:DrawChar
set /a skip2=%2+1
if "%3"=="" (
set final_%1=!final_%1:~0,%2!^|!final_%1:~%skip2%!
) else (
set final_%1=!final_%1:~0,%2!%3!final_%1:~%skip2%!
)
goto:eof
:CountChars
set charCount=0
set val=%~1
:CountChars_loop
if not "%val:~1%"=="" (
set /a charCount+=1
set val=!val:~1!
goto CountChars_loop
)
goto:eof
:FindNextNum
for /F "delims=" %%i IN ('type "%file%" ^| find /V /N ""') DO (
for /F "tokens=1,2 delims=[]" %%j IN ("%%i") DO (
set /a lineNum=%%j-1
call :FindNext_internal "%%k" %1
if /I !nextNum! equ %1 goto :eof
)
)
goto:eof
:FindNext_internal
set currLine=%~1
set linePos=0
:FindNext_internal_loop
call :NextNumInLine "%currLine%"
set /a linePos+=%spaceInterval%
if "%nextNum%"=="" goto :EOF
if /I %nextNum% equ %2 goto :EOF
set /a spaceInterval+=1
set /a linePos+=1
if /I %nextNum% GTR 9 set /a "spaceInterval+=1" & set /a linePos+=1
set currLine=!currLine:~%spaceInterval%!
goto FindNext_internal_loop
:NextNumInLine
set nextNum=
for /F %%i IN (%1) DO set /a nextNum=%%i
if "%nextNum%"=="" goto :eof
set /a spaceInterval=0
set val=%~1
:NextNumInLine_loop
if "%val:~0,1%"==" " (
set /a spaceInterval+=1
set val=!val:~1!
goto NextNumInLine_loop
)
goto :eof
И это, как вы это называете
echo off
setlocal ENABLEDELAYEDEXPANSION
call script.bat input.txt
где "input.txt" - это файл, содержащий входные данные для "программы".
PS Это на самом деле еще не оптимизировано для длины строки, я уже потратил пару часов, чтобы добраться до этой точки, и теперь мне нужно поспать... Я посмотрю, смогу ли я улучшить это завтра (в настоящее время 'script.bat'сидит на 2755 байтах)
Ребму: 218 символов
Ma L{-|\/}Qb|[sg?SBaB]Da|[feSm[TfiSrj[spAsp]iT[++Tbr]]t]Xa|[i?A]Ya|[i?FImHDa]Ca|[skPCmSCaBKfsA]wh[Jd++N][roG[xJyJ]]Bf+GwhB[JcB Ff+GiF[KcF HqXkXj VqYkYju[chCbPClEZv1[ezH2[eeHv3 4]]e?A+bRE[hV]f]]chJeFIlSCj{+}{+ }Jk Bf]wM
Я неплохо читаю и редактирую его в оригинальной латинской форме. (Хотя я использую разрывы строк!!):)
Но вот как диалект преобразуется переводчиком, когда нечувствительный к регистру трюк с "копошением" выпаривается, и к нему можно привыкнуть. Я добавлю несколько комментариев. (Подсказки: fi
это найти, fe
это foreach, sp
это космический символ, i?
индекс, hd
это голова, ch
это изменение, sk
это пропустить, pc
это выбор, bk
это перерыв, i
если, e
либо, ee
либо равен, тошнота)
; copy program argument into variable (m)atrix
m: a
; string containing the (l)etters used for walls
l: {-|\/}
; q is a "b|function" (function that takes two parameters, a and b)
; it gives you the sign of subtracting b from a (+1, -1, or 0)
q: b| [sg? sb a b]
; d finds you the iterator position of the first digit of a two digit
; number in the matrix
d: a| [fe s m [t: fi s rj [sp a sp] i t [++ t br]] t]
; given an iterator position, this tells you the x coordinate of the cell
x: a| [i? a]
; given an iterator position, this tells you the y coordinate of the cell
y: a| [i? fi m hd a]
; pass in a coordinate pair to c and it will give you the iterator position
; of that cell
c: a| [sk pc m sc a bk fr a]
; n defaults to 1 in Rebmu. we loop through all the numbers up front and
; gather their coordinate pairs into a list called g
wh [j: d ++ n] [ro g [x j y j]]
; b is the (b)eginning coordinate pair for our stroke. f+ returns the
; element at G's current position and advances G (f+ = "first+")
; advance g's iteration position
b: f+ g
wh b [
; j is the iterator position of the beginning stroke
j: c b
; f is the (f)inishing coordinate pair for our stroke
f: f+ g
; if there is a finishing pair, we need to draw a line
i f [
; k is the iterator position of the end of the stroke
k: c f
; the (h)orizontal and (v)ertical offsets we'll step by (-1,0,1)
h: q x k x j
v: q y k y j
u [
; change the character at iterator location for b (now our
; current location) based on an index into the letters list
; that we figure out based on whether v is zero, h is zero,
; v equals h, or v doesn't equal h.
ch c b pc l ez v 1 [ez h 2 [ee h v 3 4]]
; if we update the coordinate pair by the offset and it
; equals finish, then we're done with the stroke
e? a+ b re [h v] f
]
]
; whether we overwrite the number with a + or a plus and space
; depends on whether we detect one of our wall "letters" already
; one step to the right of the iterator position
ch j e fi l sc j {+} {+ }
; update from finish pair to be new begin pair for next loop iteration
j: k
b: f
]
; write out m
w m
И язык, и образец являются новыми и находятся на экспериментальной стадии. Например, ad
не мог быть использован для сложения векторов и матриц, прежде чем я изменил его, чтобы помочь с этим образцом. Но я думаю, что именно такой язык должен иметь язык, разработанный специально для гольф-кода. Это тонкая грань между "языком" и "библиотекой".
Последний источник с комментариями доступен на GitHub
Haskell, 424 символа
Текущее количество символов: 424 430451466511515516518525532541545550556569571577582586592,
import List
x%c=[(i,c)|i<-x]
l k p q|p>q=l k q p|True=head[[p,p+j..q]%c|m<-zip[k-1,k,k+1,1]"/|\\-",let (j,c)=m,mod(q-p)j==0]
w=map snd
q(k,m,x)z=w$sort$nubBy((==)&fst)$x%'+'++(concat$zipWith(l k)x$tail x)++z%'\n'++[1..m]%' '
r(z,m,x)=q(last z,m-1,w$sort x)z
u[(m,_)]n x=(-m::Int,n):x;u _ _ x=x
t(z,n,x)s|s=="\n"=(n:z,n+1,x)|True=(z,n+length s,u(reads s)n x)
y&x=(.x).y.x
main=interact$r.foldl t([],1,[]).groupBy((&&)&(>' '))
Эта версия черпает много вдохновения из оригинальной записи на Хаскеле, но вносит некоторые существенные изменения. Наиболее важно, что это представляет местоположения изображения с единственным индексом, а не парой координат.
Есть некоторые изменения:
- Теперь на входе все строки должны иметь одинаковую длину (разрешено правилами).
- Больше не нужно расширение языка
Оригинальная версия:
(потребности -XTupleSections
, и возможно -XNoMonomorphismRestriction
)
import List
b=length
f=map
g=reverse
a(x,y)" "=(x,y+1)
a(x,y)z=([y,read z]:x,y+b z)
x%y=[min x y+1..max x y-1]
j([x,y],[w,z])|y==z=f(,'-')$f(y,)$x%w|x==w=f(,'|')$f(,x)$y%z|(y<z)==(x<w)=f(,'\\')$zip(y%z)$x%w|True=f(,'/')$zip(y%z)$g$x%w
k 0='\n'
k _=' '
y&x=(.x).y.x
y?x=f y.sort.x.concat
r z=snd?(nubBy((==)&fst).g)$[((y,x),k x)|x<-[0..maximum$f b d],y<-[1..b d]]:[((y,x),'+')|[x,y]<-e]:(f j$zip e$tail e)where d=f(groupBy$(&&)&(>' '))$lines z;e=tail?f g$zipWith(f.(:))[1..]$f(fst.foldl a([],1))d
main=interact r
Объяснение:
(1) d=...
: Разбивает ввод на пробелы и числа, например
z = " 6 5\n\n1 2\n\n 4 3\n\n 7"
=> d = [[" ","6"," "," ","5"],[],["1"," "," "," "," "," "," "," ","2"],[],[" "," "," "," ","4"," "," "," ","3"],[],[" ","7"]]
(2) e=...
: Конвертирует d
в список (у, х) координат для каждого числа.
e = [[1,3],[9,3],[9,5],[5,5],[5,1],[2,1],[2,7]]
--- // 1 2 3 4 5 6 7
(3)
[((y,x),k x)|...]
пустая доска (k
возвращает пробел или\n
в зависимости от координаты х.)[((y,x),'+'))|...]
являются знаками плюс на номера.(f j$zip e$tail e)
это линии, соединяющие числа. (j
отображает пару координат в список (координата, символ), который представляет линию.)
Эти 3 компонента объединяются и фильтруются для формирования фактического вывода. Обратите внимание, что порядок важен, так что nubBy(...).g
можно сохранить только последний символ в том же месте.
AWK - 296 317 321 324 334 340
Не призер (пока), но я рад усилиям (разрывы строк для отображения). Эта новая версия использует escape-последовательности VT-100. '^[' - это всего лишь один символ, Escape!!! Вырезание и вставка не будут работать с этой версией, поскольку последовательность "^[" должна быть заменена реальным символом ESC. Чтобы сделать его удобным для форума, ESC может быть указан как "\0x1b", но он занимает слишком много места...
BEGIN{FS="[ ]"}{for(j=i=0;i<NF;j+=length(g)){if(g=$++i){x[g]=k=i+j;y[g]=NR;
m=m>k?m:k}}}END{printf"^[[2J[%d;%dH+",Y=y[i=1],X=x[1];while(a=x[++i])
{a-=X;b=y[i]-Y;t=a?b?a*b>0?92:47:45:124;A=a?a>0?1:-1:0;B=b?b>0?1:-1:0;
for(r=a?a*A:b*B;--r;){printf"^[[%d;%dH%c",Y+=B,X+=A,t}
printf"^[[%d;%dH+",Y+=B,X+=A}}
Старая стандартная версия
BEGIN{FS="[ ]"}{for(j=i=0;i<NF;j+=length(g)){if(g=$++i){x[g]=k=i+j;y[g]=NR;
m=m>k?m:k}}}END{q[X=x[1],Y=y[i=1]]=43;while(a=x[++i]){a-=X;b=y[i]-Y;
t=a?b?a*b>0?92:47:45:124;A=a?a>0?1:-1:0;B=b?b>0?1:-1:0;for(r=a?a*A:b*B;--r;
q[X+=A,Y+=B]=t);q[X+=A,Y+=B]=43}for(j=0;++j<NR;){for(i=0;i<m;){t=q[i++,j];
printf"%c",t?t:32}print}}
Теперь небольшое объяснение
# This will break the input in fields separated by exactly 1 space,
# i.e. the fields will be null or a number.
BEGIN{FS="[ ]"}
# For each line we loop over all fields, if the field is not null
# it is a number, hence store it.
# Also account for the fact the numbers use space.
# Also, find the maximum width of the line.
{
for(j=i=0;i<NF;j+=length(g)){
if(g=$++i){
k=j+i;x[g]=k;y[g]=NR;m=m>k?m:k
}
}
}
# Once we have all the data, let start cooking.
END{
# First, create a matrix with the drawing.
# first point is a +
q[X=x[1],Y=y[i=1]]=43;
# loop over all points
while(a=x[++i]){
# Check next point and select character
# If a == 0 -> -
# If b == 0 -> |
# If a and b have same sign -> \ else /
a-=X;b=y[i]-Y;t=a?b?a*b>0?92:47:45:124;
# there is no sgn() function
A=a?a>0?1:-1:0;B=b?b>0?1:-1:0;
# Draw the line between the points
for(k=0;++k<(a?a*A:b*B);){
q[X+=A,Y+=B]=t
}
# store + and move to next point
q[X+=A,Y+=B]=43
}
# Now output all lines. If value in point x,y is 0, emit space
for(j=0;++j<NR;){
for(i=0;i<m;){
t=q[i++,j];printf("%c",t?t:32)
}
print
}
}
С, 386
402 386 символов на C. Символы новой строки после первого предназначены только для удобства чтения.
#include <stdio.h>
int x[101],y[101],c=1,r,w,h,b,i,j,k,m,n;
int main(){
while((b=getchar())-EOF)
b-' '?b-'\n'?ungetc(b,stdin),scanf("%d",&b),x[b]=c++,y[b]=h,c+=b>9:(w=c>w?c:w,++h,c=1):++c;
for(r=0;r<h&&putchar('\n');++r)
for(c=0;c<w;++c){
for(b=' ',i=2,m=x[1]-c,n=y[1]-r;j=m,k=n,m=x[i]-c,n=y[i]-r,x[i++];)
b=j|k&&m|n?j*m>0|k|n?k*n<0?(j-k|m-n?j+k|m+n?j|m?b:'|':'/':'\\'):b:'-':'+';
putchar(b);
}
}
Intel Assembler
Собранный размер: 506 байт
Источник: 2252 байта (эй, это не тривиальная проблема)
Для сборки: Используйте A86 Для запуска: Протестировано с WinXP DOS box. мольба jtd.com < input > output
mov ax,3
int 10h
mov ax,0b800h
mov es,ax
mov ah,0bh
int 21h
mov bx,255
cmp al,bl
mov dh,bh
mov si,offset a12
push offset a24
je a1
mov si,offset a14
a1: inc bl
a2: mov dl,255
call si
cmp al,10
jb a4
a3: cmp al,10-48
jne a1
inc bh
mov bl,dh
jmp a2
a4: mov dl,al
call si
cmp al,10
jae a5
mov ah,dl
aad
mov dl,al
a5: mov di,dx
mov ch,al
shl di,2
mov [di+a32],bx
cmp bl,[offset a30]
jb a6
mov [offset a30],bl
a6: cmp bh,[offset a31]
jb a7
mov [offset a31],bh
a7: push offset a19
mov al,80
mul bh
add al,bl
adc ah,0
add ax,ax
lea di,[di+2+a32]
mov [di],ax
add di,2
cmp di,[a22-3]
jbe a8
mov [a22-3],di
mov [a25-3],di
a8: mov di,ax
mov al,dl
aam
cmp ah,0
je a10
a9: add ah,48
mov es:[di],ah
add di,2
a10:add al,48
mov es:[di],al
mov al,ch
inc bl
jmp a3
a11:jmp si
a12:mov ah,0bh
int 21h
cmp al,255
jne a15
mov ah,8
int 21h
a13:cmp al,13
je a11
sub al,48
ret
a14:mov ah,1
int 21h
cmp al,26
jne a13
mov si,offset a15
ret
a15:cmp dl,255
je a16
mov al,32
ret
a16:mov si,offset a32 + 4
lodsw
mov cx,ax
mov dx,ax
lodsw
mov di,ax
mov b es:[di],1
mov bp,0f000h
call a26
add sp,6
mov bx,[a22-3]
mov ax,[offset a31]
inc ax
a17:mov bp,[offset a30]
a18:mov b[bx],32
inc bx
dec bp
jnz a18
mov w[bx],0a0dh
add bx,2
dec ax
jnz a17
mov b[bx],'$'
add w[a30],2
a19:lodsw
xchg ax,dx
cmp ah,dh
lahf
mov bl,ah
cmp al,dl
lahf
shr bl,6
shr ah,4
and ah,12
or bl,ah
mov bh,0
shl bx,3
a20:mov b es:[di],43
a21:mov al,b[a30]
mul ch
add al,cl
adc ah,0
mov bp,ax
mov b[bp+100h],43
a22:add di,[bx + a29]
add cl,[bx + a29 + 4]
add ch,[bx + a29 + 6]
mov b es:[di],1
mov al,[bx + a29 + 2]
mov [a21-1],al
mov [a22-1],al
mov bp,01000h
call a26
cmp di,[si]
jne a20
mov al,es:[di+2]
sub al,48
cmp al,10
jae a23
mov b es:[di+2],0
a23:mov b[a21-1],43
mov b[a22-1],43
mov b es:[di],43
lodsw
ret
a24:mov al,b[a30]
mul ch
add al,cl
adc ah,0
mov bp,ax
mov b[bp+100h],43
a25:mov dx,[a22-3]
mov ah,9
int 21h
ret
a26:pusha
a27:mov cx,0ffffh
a28:loop a28
dec bp
jnz a27
popa
ret
a29:dw -162,92,-1,-1,-2,45,-1,0,158,47,-1,1,0,0,0,0,-160,124,0,-1
a30:dw 0
a31:dw 0,0,0,160,124,0,1,0,0,0,0,-158,47,1,-1,2,45,1,0,162,92,1,1
a32:
Интересные функции: самоизменяющийся код, анимированный вывод (второй пример работает, но слишком велик для отображения), злоупотребление 'ret' для реализации счетчика цикла, интересный способ определения направления линии / движения.
Powershell, 328 304 символа
$i=$l=0;$k=@{}
$s=@($input|%{[regex]::matches($_,"\d+")|%{$k[1*$_.Value]=@{y=$l
x=$_.Index}};$l++;""})
while($a=$k[++$i]){
if($i-eq1){$x=$a.x;$y=$a.y}
do{$d=$a.x.CompareTo($x);$e=$a.y.CompareTo($y)
$s[$y]=$s[($y+=$e)].PadRight($x+1).Remove($x,1).Insert(($x+=$d),
"\-/|+|/-\"[4+$d*3+$e])}while($d-or$e)}$s
и вот довольно печатная версия с комментариями:
# Usage: gc testfile.txt | dots.ps1
$l=$i=0 # line, dot index (used below)
$k=@{} # hashtable that maps dot index to coordinates
# Apply regular expression to each line of the input
$s=@( $input | foreach{
[regex]::matches($_,"\d+") | foreach{
# Store each match in the hashtable
$k[ 1*$_.Value ] = @{ y = $l; x = $_.Index }
}
$l++; # Next line
"" # For each line return an empty string.
# The strings are added to the array $s which
# is used to produce the final output
}
)
# Connect the dots!
while( $a = $k[ ++$i ] )
{
if( $i -eq 1 ) # First dot?
{
# Current position is ($x, $y)
$x = $a.x;
$y = $a.y
}
do
{
$d = $a.x.CompareTo( $x ) # sign( $a.x - $x )
$e = $a.y.CompareTo( $y ) # sign( $a.y - $y )
$c = '\-/|+|/-\'[ 4 + $d * 3 + $e ] # character '
# Move
$x += $d
$y += $e
# "Replace" the charcter at the current position
# PadRight() ensures the string is long enough
$s[ $y ]=$s[ $y ].PadRight( $x+1 ).Remove( $x, 1 ).Insert( $x, $c )
} while( $d -or $e ) # Until the next dot is reached
}
# Print the resulting string array
$s
F#, 725 символов
open System
let mutable h,s,l=0,Set.empty,Console.ReadLine()
while l<>null do
l.Split([|' '|],StringSplitOptions.RemoveEmptyEntries)
|>Seq.iter(fun t->s<-s.Add(int t,h,(" "+l+" ").IndexOf(" "+t+" ")))
h<-h+1;l<-Console.ReadLine()
let w=Seq.map(fun(k,h,x)->x)s|>Seq.max
let o=Array2D.create h (w+1)' '
Seq.sort s|>Seq.pairwise|>Seq.iter(fun((_,b,a),(_,y,x))->
let a,b,x,y=if b>y then x,y,a,b else a,b,x,y
o.[b,a]<-'+'
o.[y,x]<-'+'
if b=y then for x in(min a x)+1..(max a x)-1 do o.[y,x]<-'-'
elif a=x then for h in b+1..y-1 do o.[h,x]<-'|'
elif a<x then for i in 1..y-b-1 do o.[b+i,a+i]<-'\\'
else for i in 1..y-b-1 do o.[b+i,a-i]<-'/')
for h in 0..h-1 do
for x in 0..w do printf"%c"o.[h,x]
printfn""
Условные обозначения:
h = height
s = set
l = curLine
w = (one less than) width
o = output array of chars
Строки 1-6: я храню набор кортежей (number, lineNum, xCoord); когда я читаю в каждой строке ввода, я нахожу все числа и добавляю их в набор.
Строка 7-8: Затем я создаю массив выходных символов, инициализированный для всех пробелов.
Строка 9: сортировка набора (по номеру), затем взять каждую соседнюю пару и...
Строки 10-16: ... сортировать так, чтобы (a,b) была самой высокой из двух точек, а (x,y) - другой. Поместите знаки "+", а затем, если горизонтальный, нарисуйте это, иначе, если вертикальный, нарисуйте это, иначе нарисуйте правильную диагональ. Если ввод не "действителен", то кто знает, что происходит (этот код был усеян "утверждениями" до того, как я его сыграл в гольф).
Строки 17-19: распечатать результат
Питон - 381
import re
b=list(iter(raw_input,''))
c=sum((zip([i]*999,re.finditer('\\d+',x))for i,x in enumerate(b)),[])
d=sorted((int(m.group()),i,m.start())for i,m in c)
e=[[' ']*max(map(len,b))for x in b]
for(t,u,v),(x,y,z)in zip(d,d[1:]+d[-1:]):
e[u][v]='+'
while u!=y or v!=z:i,j=(u<y)-(u>y),(v<z)-(v>z);u+=i;v+=j;e[u][v]=['|','/\\-'[(i==j)+2*(i==0)]][j!=0]
print'\n'.join(map(''.join,e))
C#, 422 символа
758 754 641 627 584 546 532 486 457 454 443 440 422 символа (в следующий раз, возможно, я не отправлю так скоро.)
using A=System.Console;class B{static int C,o,d,e,G,O=1,f,F,u,n;static
void Main(){var s=A.In.ReadToEnd();A.Clear();while(++u<s.Length){f++;if
(s[u]<32){u++;F++;f= 0;}if(s[u]>32){if(int.Parse(s[u]+""+s[++u])==O){o=
e>f?1:f>e?-1:0;C=d>F?1:F>d?-1:0 ;G=e+o;n=d+C;if(O++>1)while(n!=F||G!=f)
{A.SetCursorPosition(G-=o,n-=C);A.Write( "+/-|\\"[n==d&&G==e?0:n==F&&G
==f?0:C+o==0?1:C==0?2:o==0?3:4]);}e=f;d=F;F=0;f=u=-1 ;}f++;}}A.Read();}}
Использование: запустите, вставьте (или введите) ввод, убедитесь, что последняя строка завершена, нажмите CTRL-Z или F6, нажмите Enter.
Отформатированная, но все еще в основном неразборчивая версия:
using A = System.Console;
class B
{
// code golf fun!
static int C, o, d, e, G, O = 1, f, F, u, n;
static void Main()
{
// read the input into a string char by char until EOF
var s = A.In.ReadToEnd();
A.Clear(); // clear console, ready to draw picture
// O is the "dot" number we're looking for
// f is current column
// F is current row
// loop over the field looking for numbers sequentially
// until no more are found
while (++u < s.Length)
{
f++;
// any char <32 is expected to be a CR/LF
// increment the current row and reset the current column
if (s[u] < 32)
{
u++; // skip the other half of the CR/LF pair
F++; // next row
f = 0; // column reset
}
// any char >32 is expected to be a number
if (s[u] > 32)
{
// parse the current + next char and see if it's
// the number we want
if (int.Parse(s[u] + "" + s[++u]) == O)
{
// set up coordinates, compare X1 with X2
// and Y1 with Y2 to figure out line direction
// horizontal direction (same as o=e.CompareTo(f))
o = e > f ? 1 : f > e ? - 1 : 0;
// vertical direction (same as C=d.CompareTo(F))
C = d > F ? 1 : F > d ? - 1 : 0;
// initial offsets compensate for off-by-one
G = e + o;
n = d + C;
// draw the line (except for the very first dot)
if (O++ > 1)
while (n != F || G != f)
{
// update coords and write desired char
A.SetCursorPosition(G -= o, n -= C);
// this lovely line decides which char to
// print, and prints it
A.Write(
"+/-|\\"[n == d && G == e ? 0 : n == F && G
== f ? 0 : C + o == 0 ? 1 : C == 0 ? 2 : o
== 0 ? 3 : 4]);
}
// remember end point of this line, to use as start point
// of next line
e = f;
d = F;
// reset current row (F), column (f), field position (u)
F = 0;
f = u = -1;
}
// bump current column because we parse 2 chars when we
// find a dot
f++;
}
}
A.Read(); // prevent command prompt from overwriting picture
}
}
C#, 638 символов
using System;
using System.Linq;
using System.Text.RegularExpressions;
class C
{
static void Main()
{
int i=0,j;
var p = Console.In.ReadToEnd()
.Split('\n')
.SelectMany(
r =>
{
i++; j =0;
return Regex.Matches(r, "\\s+(\\d+)").Cast<Match>()
.Select(m => { j += m.Length; return new { X = j, Y = i-1, N = int.Parse(m.Groups[1].Value) }; });
}
).OrderBy(a=>a.N).ToList();
var W = p.Max(a => a.X)+1;
var k = new char[W*i+W];
i = 0;
while (i < p.Count)
{
var b = p[i > 0 ? i - 1 : 0]; var a = p[i];
int h = a.Y - b.Y, w = a.X - b.X;
var s = "|-/\\"[h == 0 ? 1 : w == 0 ? 0 : h / w > 0 ? 3 : 2];
while ((h | w) != 0) { k[b.X + w + W * (b.Y + h)] = s; h -= h.CompareTo(0); w -= w.CompareTo(0); }
k[a.X + a.Y * W] = '+';
k[W * ++i] = '\n';
}
Console.Write(k);
}
}
Вот оно!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int sign(int x) {
if (x < 0)
return -1;
if (x > 0)
return +1;
return 0;
}
#define MAX_ROWS 100
#define MAX_COLS 100
#define MAX_DIGITS 100
int main(void)
{
// Read in the digits
int number[MAX_DIGITS][2];
int rows = 0;
int cols = 0;
char row[MAX_COLS];
int maxvalue = 0;
int i, j, value, x;
for (i = 0; i < MAX_ROWS; i++) {
if (row != fgets(row, MAX_COLS, stdin))
break;
value = 0;
for (j=0; row[j] != 0; j++) {
if (row[j] >= '0' && row[j] <= '9') {
x = j;
value = 0;
do {
value = 10*value + (row[j]-'0');
j++;
} while (row[j] >= '0' && row[j] <= '9');
number[value][0] = i;
number[value][1] = x;
if (maxvalue < value) maxvalue = value;
if (rows < i+1) rows = i+1;
if (cols < x+1) cols = x+1;
}
}
}
// Create an empty field
char field[rows][cols];
memset(field, ' ', rows*cols);
char lines[] = "\\|/-+-/|\\";
int dr,dc;
// Draw the numbers and lines
field[number[1][0]][number[1][1]] = '+';
for (i = 2; i <= maxvalue; ++i) {
int r = number[i-1][0];
int c = number[i-1][1];
int rt = number[i][0];
int ct = number[i][1];
dr = sign(rt-r);
dc = sign(ct-c);
char line = lines[(dr+1)*3+dc+1];
while (r != rt || c != ct) {
r += dr;
c += dc;
field[r][c] = line;
}
field[r][c] = '+';
}
for (i = 0; i < rows; ++i) {
for (j = 0; j < cols; ++j)
putchar(field[i][j]);
putchar('\n');
}
return 0;
}
C++ 637
#include <iostream>
#include <string>
#include <vector>
#define S(x)((x)<0?-1:x>0?1:0)
using namespace std;enum{R=100,C=100,D=100};int main(){string s;
int N[D][2],M=0,q=0,p=0,i,j,V,L,a,b;for(i=0;j=0,(i<R)&&getline(cin,s);i++)
while((j=s.find_first_not_of(" ",j))<=s.size()){L=sscanf(&s[j],"%d",&V);
N[V][0]=i;N[V][1]=j;if(M<V)M=V;if(q<=i)q=i+1;if(p<=j)p=j+1;j+=L+1;}
string F(q*p,' '),l="\\|/-+-/|\\";F[p*N[1][0]+N[1][1]]='+';for(i=2;i<=M;++i){
int r=N[i-1][0],c=N[i-1][1],d=N[i][0],e=N[i][1];for(a=S(d-r),b=S(e-c);r!=d||c!=e;)
r+=a,c+=b,F[p*r+c]=l[(a+1)*3+b+1];F[p*r+c]='+';}for(i=0;i<q;i++)
cout<<string(&F[i*p],p)+"\n";}
С отступом и с несколько более значимыми именами, это выглядит так:
#include <iostream>
#include <string>
#include <vector>
#define S(x)((x)<0?-1:x>0?1:0)
using namespace std;
enum{R=100,C=100,D=100};
int main(){
string s;
int N[D][2],M=0,rs=0,cs=0,i,j,V,L,dr,dc;
for(i=0;j=0,(i<R)&&getline(cin,s);i++)
while((j=s.find_first_not_of(" ",j))<=s.size()){
L=sscanf(&s[j],"%d",&V);
N[V][0]=i;
N[V][1]=j;
if(M<V)M=V;
if(rs<=i)rs=i+1;
if(cs<=j)cs=j+1;
j+=L+1;
}
string F(rs*cs,' '),lines="\\|/-+-/|\\";
F[cs*N[1][0]+N[1][1]]='+';
for(i=2;i<=M;++i){
int r=N[i-1][0],c=N[i-1][1],rt=N[i][0],ct=N[i][1];
for(dr=S(rt-r),dc=S(ct-c);r!=rt||c!=ct;)
r+=dr,c+=dc,F[cs*r+c]=lines[(dr+1)*3+dc+1];
F[cs*r+c]='+';
}
for(i=0;i<rs;i++)
cout<<string(&F[i*cs],cs)+"\n";
}
Несмотря на поверхностные различия, это явная кража кода morotspaj.
Я не могу сделать многострочное в комментарии, поэтому я продемонстрирую здесь. В следующих примерах расстояние (x1,x2) == расстояние (y1,y2):
+
|\
+-+
+
|\
| \
+--+
+
|\
| \
| \
+---+
С правилами, как объяснено, расстояние (x1,x2) == расстояние (y1,y2)+2:
+\
| \
+--\+
+\
| \
| \
+---\+
+\
| \
| \
| \
+----\+