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


  1. baldr
    14.11.2022 18:56
    +3

    Думаю вы легко ошибки в стиле программирования и не только.

    Думаю вы просто смысл и не только.


  1. lynxp9
    14.11.2022 19:05
    +1

    Можно пример fen, чтобы посмотреть как оно работает?


    1. Askalite Автор
      14.11.2022 19:12

      3r1kq1/p2prp1p/5RpP/8/2Q5/1B4P1/P4PK1/8


      1. lynxp9
        14.11.2022 19:23

        Классно) Я когда-то давно делал пет-проджект с полезной нагрузкой в виде шашек. Именно по шашкам трудно было найти информацию. А ваша статья может облегчить жизнь какому-то студенту в написании лабораторной или курсовой)


  1. Rsa97
    14.11.2022 20:15
    +11

    const 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>
    // .  .  .  .  .  .  .  .  .


  1. infund
    14.11.2022 21:16
    +2

    Давным-давно была шахматная программа Sargon III. В ранних ее версиях и доска и фигуры отрисовывались досовской псевдографикой. Весь геймплей происходил в текстовом режиме 80х25.

    Геймплей


    1. Nikita_64
      15.11.2022 00:56

      О да, на Apple II тоже был, кажется, Sargon II.


    1. LuchS-lynx
      15.11.2022 07:58

      В MS DOS через ассемблер, да и на Спектруме можно было программировать некоторые символы, заменяя текущее отображение на необходимое, т.е., к примеру, перерисовать шрифт, или использовать символы вместо спрайтов


      1. Vaitek
        15.11.2022 09:12

        В детстве на 286ом у меня была не помню какая программа, которая позволяла рисовать шрифты под DOS. И не помню какая программа которая позволяла их загружать. Было забавно. Но все умерло на 40мб жестком диске)))


        1. vesper-bot
          15.11.2022 10:57

          Загружались они через mode con cp prep=((NUMBER),,FILE) плюс mode con cp sel=NUMBER вероятнее всего. А программа для шрифтов — досадно, что потерялась.


          1. LuchS-lynx
            15.11.2022 13:26

            у меня, возможно, осталось... надо будет завтра поднять школьные архивы, но если только в исходниках.


  1. 1nsan31nsId3
    15.11.2022 09:01

    Можно использовать ТЕХ формат для этого.



  1. roqin
    15.11.2022 22:12

    Да и где-то в глубинах памяти есть заметка: "наложение символа на символ спецсимволом работает не со всеми символами".

    А у меня в голове отложилось что это работает только со спецсимволами (Combining Marks или что-то вроде того). Множество чёрных и белых квадратиков есть, только вот как наложение сделать? Там вроде и подходящая диакритика есть, только вот выглядят сейчас не очень: ♔ + ◌⃞ (U+20DE) = ♔⃞. На чёрное поле фигуру только с помощью голого текста поставить не получится.

    Эмодзи раскрасить можно (????????????????), только с шахматами это не работает. Пичалька ????