javascript

Шахматная доска в псевдографике

  • вторник, 15 ноября 2022 г. в 00:43:40
https://habr.com/ru/post/699118/
  • JavaScript
  • Программирование


Я люблю шахматы, и есть некоторые места в интернете, где я не могу просто взять и вставить картинку, чтобы наглядно показать шахматную позицию. До определённого момента это меня не волновало, но однажды космический луч ударил мне в мозг и активировал небольшой нейронный импульс, который лавинообразно активировал сдерживаемые до этого момента мысли. И мне пришла в голову мысль, а почему бы не представить шахматную доску в виде 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