Я люблю шахматы, и есть некоторые места в интернете, где я не могу просто взять и вставить картинку, чтобы наглядно показать шахматную позицию. До определённого момента это меня не волновало, но однажды космический луч ударил мне в мозг и активировал небольшой нейронный импульс, который лавинообразно активировал сдерживаемые до этого момента мысли. И мне пришла в голову мысль, а почему бы не представить шахматную доску в виде ASCII Art?
TL; DR
Процесс разработки одной функции. Вряд ли интересен опытным программистам, но вполне подойдёт новичкам, чтобы те представляли, как вообще происходит программирование. Опытные программисты найдут много недостатков и для них статья носит больше развлекательных характер.
Что мне нужно? Или Техническое Задание
Шахматная позиция обычно задаётся с помощью нотации Форсайта – Эдвардса. Буквами K, Q, R, B, N, P обозначаются белые фигуры, а в нижнем регистре – чёрные. Косая черта / разделяет горизонтали, а числа обозначают количество пустых клеток. Это всё, что надо мне знать о нотации FEN.
Что мне необходимо? Показать эту позицию наглядно с помощью шахматных фигур на шахматной доске. И всё это используя только символы. Осталось только нарисовать, что я хочу видеть. Точнее напечатать.
Сначала я решил найти готовые решения и они мне не очень понравились. Попытка перевести картинку в псевдографику выглядела убого и получившаяся картинка занимала слишком много места. К тому же я не мог сделать текст моноширинным, чтобы он выглядел красиво.
Поиск решения, наглядное представление
И первым делом я скопипастил скопировал из Википедии юникод символы шахмат в ряд, чтобы посмотреть, как это будет выглядеть:
♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜
На мой дилетантский дизайнерский взгляд это уже выглядит неплохо. Почему бы не сделать шахматную доску просто из пробелов и шахматных символов?
♜ ♞ ♝ ♛ ♚ ♞
♝ ♜
О нет, это выглядит ужасно! Глазу не за что зацепиться, чтобы привязаться к координатам. И есть маленькая проблема с пробелами. Но несущественна на фоне этого ужаса.
Я попытался сделать разделители:
|♜ |♞
Это всё ещё выглядит плохо. Нужно что-то, что я буду воспринимать лучше. Я долго копался в интернете выискивая решение и даже наткнулся на это:
# # #
# #
# # #
Это типа шахматная доска, но я не знаю, как символ рисовать поверх символа средствами символьных команд, точнее знаю, но мне было лень в этом разбираться. Да и где-то в глубинах памяти есть заметка: "наложение символа на символ спецсимволом работает не со всеми символами". Видимо когда-то в прошлом я этим занимался и записал это обобщение в мозг. И как обычно без конкретики, так что придётся гуглить, если мне будет интересно разобраться в этом.
Я не знаю в какой момент, но мой разум обратил внимание на точки. Я нашёл символ широкого пробела, я скопировал его в буфер обмена и принялся рисовать. Результат мне понравился:
. ♜. ♞. ♝. ♛. ♚. ♝. ♞. ♜.
. ♟. ♟. ♟. ♟. ♟. ♟. ♟. ♟.
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. ♙. ♙. ♙. ♙. ♙. ♙. ♙. ♙.
. ♖. ♘. ♗. ♕. ♔. ♗. ♘. ♖.
Это было то, что мне нужно. Минималистично, кратко, понятно. На мой дилетантский дизайнерский взгляд это красиво. Осталось превратить строку в виде "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR" в строку выше.
Макет, интерфейс и обвязка
Первым делом я в виде html странички создал интерфейс. Очень простенький поле ввода и кнопка:
<div>
<form name="publish">
<input type="text" name="message" id="input" />
</form>
<button name="send" id = "click" >Отправить</button>
</div>
<div id="result" ></div>
Как по мне больше ничего не нужно. Это идеальный минималистичный интерфейс для одной функции. Есть поле ввода, есть кнопка, есть поле вывода.
Программирование
Программировал я на js давно и всё позабыл. Пришлось вспоминать на ходу. Я создал небольшую функцию. Она очень простенькая. Get Result:
getres(){};
Так я избавился от проблемы чистого листа и начал думать, что эта функция должна делать. По идее я должен сделать документацию в коде, но я делал её в голове, поскольку не помнил как делают комментарии в js, а искать и разбираться было лень. Итак, функция делает: читает поле ввода, вызывается по нажатию кнопки, пишет результат в div элемент, где выводится результат. И ещё, важная вещь:
//TODO сделать документацию к функции. Даже если она простая и понятная.
Далее я в теле функции добавил необходимые мне переменные с которыми я буду работать:
var m = document.getElementById("input").value;
var result ="";
var l=m.length;
Я считал поле ввода, теперь надо вывести результат:
document.getElementById("result").innerHTML = result;
Отлично, у нас есть переменная m из которой мы читаем по FEN и переменная result, куда мы пишем результат. И первым делом пишем точку:
result += ".";
Тестируем, исправляем ошибки. О, вот тут начинается веселье Так, например у меня была проблема с тем, что при нажатии на кнопку input у меня перезагружалась страница, пришлось вывести кнопку из формы. То что вы видели ранее это уже готовое решение к которому я пришёл через ошибки. Я забыл js и html, поэтому в моём коде было много копирования из других источников. Которые я исправлял и допиливал.
Дальше я просто сделал цикл, чтобы пройтись по всем элементам строки m, но самое главное я внутрь цикла добавил базовую конструкцию из условий:
for (var i=0; i < l; i++)
{
if (m[i] == "")
{
result += "";
}
else
}
Я читаю символ из m, сравниваю и пишу результат. Теперь делаем копирование и вставить, копипастим:
for (var i=0; i < l; i++)
{
if (m[i] == "")
{
result += "";
}
else if (m[i] == "")
{
result += "";
}
else if (m[i] == "")
{
result += "";
}
else
}
Думаю на этом моменте у некоторых более опытных разработчиков где-то в глубине разума раздастся истошный испуганный визг или иная проявится иная негативная реакция, но они опытные разработчики и не такое видели. Но пока ничего плохого не произошло, весь ужас будет дальше. От себя добавлю: при копировании кода нужно быть очень внимательным, иначе будет нечто такое:
x=+w*n;
y=+w*n; //<-тут вместо w должен быть h
Следующим шагом мы делаем зарядку. Разминаем шею, ноги, глаза. И приступаем к заполнению необходимых полей, то есть делаем по символам дешифровку FEN
if (m[i] == "1")
{
result += " .";
}
else if (m[i] == "2")
{
result += " . ."; <- 2 это два больших пробела с точкой :D
}
else if (m[i] == "3")
{...
...
else if (m[i] == "/")
{
result += "\n<br>."; <- тут я сначала забыл добавить тэг <br>
}
else if (m[i] == " ")
{
break;
};
Думаю вы легко нашли ошибки в стиле программирования и не только.
Тяп-ляп и в продакшн? Почему бы и нет
Осталось опубликовать свой продукт, а именно, маленькое приложение преобразующее FEN в UTF арт. Здесь должна быть история проблем с созданием репозитория и его наполнением путём переноса файлов со смартфона, но я это опущу. В общем, когда всё получилось, то я испытал незабываемое удовольствие, сравнимое с удовольствием от build successful.
Выводы и важные примечания
Первое: я сначала делал внешний вид, а потом переходил к коду, в этом есть своё удобство, но в будущем сулит большие проблемы для разработчика.
Второе: невнимательный
копипасткопирование это зло.Третье: я не знаю как код поведёт себя в случае некорректного ввода. Безопасность моего проекта примерно отрицательная.
Четвёртое: не забыть прокомментировать функцию, пройдёт время и я вообще забуду что это такое, какие-то непонятные закорючки и шахматные символы. Вы, если новичок, не представляете насколько облегчается жизнь даже если комментарии к коду очевидны, как минимум это экономит некоторое время, которое обычно тратится на анализ языка программирования, анализ кода и того что он делает, когда ты забыл, что делает код, хотя раньше тебе казалось это очевидным. Вы просто читаете обобщённый комментарий не тратя ментальные силы на расшифровку очевидного кода.
Пятое: некоторые вещи делать плохо, но можно, если лень придумывать решение.
else if (m[i] == "2")
{
result += " . .";
Я более чем уверен, что есть более элегантное и красивое решение без дополнительных ветвлений.
Заключение
Мне было приятно написать простенький код для решения одной задачи. Однако вряд ли я его буду поддерживать. Мне достаточно того функционала, что есть на данный момент. Надеюсь вы вынесли для себя что-то полезное, нашли места где я что-то сделал не так, да и просто приятно провели время за чтением. Не говоря уже о том, что некоторые могут попытаться улучшить мой код и попробовать отправить мне изменения, получив первый опыт изменения чужого кода. Благодарю за внимание.
Проект на Github:
https://github.com/Askalite/FenTransform
Страничка с очень маленьким приложением:
https://askalite.github.io/FenTransform/
Пример FEN: 3r1kq1/p2prp1p/5RpP/8/2Q5/1B4P1/P4PK1/8 w - - 1 53
Комментарии (14)
lynxp9
14.11.2022 19:05+1Можно пример fen, чтобы посмотреть как оно работает?
Askalite Автор
14.11.2022 19:123r1kq1/p2prp1p/5RpP/8/2Q5/1B4P1/P4PK1/8
lynxp9
14.11.2022 19:23Классно) Я когда-то давно делал пет-проджект с полезной нагрузкой в виде шашек. Именно по шашкам трудно было найти информацию. А ваша статья может облегчить жизнь какому-то студенту в написании лабораторной или курсовой)
Rsa97
14.11.2022 20:15+11const figures = { 'p': '♟', 'r': '♜', 'n': '♞', 'b': '♝', 'q': '♛', 'k': '♚', 'd': '⛂', 'm': '⛃' 'P': '♙', 'R': '♖', 'N': '♘', 'B': '♗', 'Q': '♕', 'K': '♔', 'D': '⛀', 'M': '⛁' 'e': ' ', 'f': '.', '/': "<br>\n", }; const decodeField = (code) => code.replaceAll( /[prnbqk]/ig, (c) => `${figures.f}${figures[c]}`, ).replaceAll( /\d/g, (d) => `${figures.f}${figures.e}`.repeat(+d), ).replaceAll('/', `${figures.f}${figures['/']}`) + figures.f; decodeField('3r1kq1/p2prp1p/5RpP/8/2Q5/1B4P1/P4PK1/8'); // . . . .♜. .♚.♛. .<br> // .♟. . .♟.♜.♟. .♟.<br> // . . . . . .♖.♟.♙.<br> // . . . . . . . . .<br> // . . .♕. . . . . .<br> // . .♗. . . . .♙. .<br> // .♙. . . . .♙.♔. .<br> // . . . . . . . . .
infund
14.11.2022 21:16+2Давным-давно была шахматная программа Sargon III. В ранних ее версиях и доска и фигуры отрисовывались досовской псевдографикой. Весь геймплей происходил в текстовом режиме 80х25.
Геймплей
LuchS-lynx
15.11.2022 07:58В MS DOS через ассемблер, да и на Спектруме можно было программировать некоторые символы, заменяя текущее отображение на необходимое, т.е., к примеру, перерисовать шрифт, или использовать символы вместо спрайтов
Vaitek
15.11.2022 09:12В детстве на 286ом у меня была не помню какая программа, которая позволяла рисовать шрифты под DOS. И не помню какая программа которая позволяла их загружать. Было забавно. Но все умерло на 40мб жестком диске)))
vesper-bot
15.11.2022 10:57Загружались они через mode con cp prep=((NUMBER),,FILE) плюс mode con cp sel=NUMBER вероятнее всего. А программа для шрифтов — досадно, что потерялась.
LuchS-lynx
15.11.2022 13:26у меня, возможно, осталось... надо будет завтра поднять школьные архивы, но если только в исходниках.
roqin
15.11.2022 22:12Да и где-то в глубинах памяти есть заметка: "наложение символа на символ спецсимволом работает не со всеми символами".
А у меня в голове отложилось что это работает только со спецсимволами (Combining Marks или что-то вроде того). Множество чёрных и белых квадратиков есть, только вот как наложение сделать? Там вроде и подходящая диакритика есть, только вот выглядят сейчас не очень: ♔ + ◌⃞ (U+20DE) = ♔⃞. На чёрное поле фигуру только с помощью голого текста поставить не получится.
Эмодзи раскрасить можно (????????????????), только с шахматами это не работает. Пичалька ????
baldr
Думаю вы просто смысл и не только.