Игра в бисер на Python
Давай сыграем в игру?
Что вообще такое «игра в бисер»?
В бытовом языке этот фразеологизм означает нечто заумное, переусложненное и бесполезное. Выражение восходит к роману писателя Германа Гессе, за который тот на минуточку получил Нобелевскую премию. В романе изображен вымышленный мир, устроенный таким образом, что посреди обычных государств в мире основана специальная страна интеллектуалов — Касталия. Живущие в ней люди могут, не задумываясь о куске хлеба, посвятить себя ответам на лишенные практической пользы гуманитарные вопросы вроде трактовки какой-нибудь строки древнего стихотворения. Все остальные государства терпят существование Касталии и регулярно скидываются на ее содержание (ну чисто как ООН), а сама Касталия немного воспитывает учителей языков, литературы, музыки и математики для внешнего мира, но по большому счету никому ничего не должна. Предельным выражением оторванности от практической жизни служит как раз та самая игра в бисер — самая авторитетная внутри Касталии, но мало кому понятная за ее пределами деятельность.
Привыкшим к бодрому экшону роман будет читать трудновато: там мало что происходит. Герой Йозеф Кнехт учится в разных заведениях, ходит туда и сюда, разговаривает разговоры, и сначала добивается больших успехов, продвигаясь наверх внутренней иерархии Касталии, становится самым главным по игре в бисер (а это значит — самым главным вообще), но потом разочаровывается во всем и покидает страну, сочтя, что лучше жить рядом с простыми людьми. Мораль, в общем, простая, но сформулированная немецким писателем в разгар Второй мировой войны (1943 год на дворе), она становится социально значимым высказыванием: интеллектуалам, несмотря на соблазн отгородиться от мира и погрузиться в свои манускрипты, не следует терять контакта с происходящим вокруг.
Правда, беспомощный в обычной жизни Кнехт (спойлер!) довольно быстро погибает за пределами Касталии, но это уже совсем другая история.
Роман дожил до наших дней и еще не сдан в глубокий архив не благодаря заложенным в сюжете простым истинам и не благодаря занудным монологам персонажей Гессе. Жемчужина произведения — как раз идея той самой игры. Как она выглядит и в чем заключается, автор нигде не пишет. И в этом, по всей видимости, одна из причин ее привлекательности. Опиши Гессе все в подробностях, игра тут же лишилась своего ореола таинственности и креативного потенциала. А сейчас читатель сам должен придумать, что там к чему, и это весело.
Как все это должно выглядеть?
Я вижу игру в бисер как манипуляцию знаками (наличие собственно «бисера» в игре всего лишь метафора). Знаки — это такие объекты, у которых есть материальная репрезентация («означающее») и стоящие за ней смыслы («означаемое»). Означающее для буквы «А» — это то, как эта буква выглядит, а означаемое локализуется в пределах какой-нибудь конкретной семиотической системы, например, графической системы русского языка, где есть соответствующий этой букве звук.
Сам процесс игры скорее является игрой в том же значении, что и «игра на музыкальном инструменте», то есть это исполнение заранее составленного произведения, а не игра в смысле соперничества каких-то игроков. Хотя для Гессе первично то значение «игры», которое связано с деятельностью, не приносящей пользы, где процесс важнее результата.
Стало быть, у игры в бисер должен быть какой-то алфавит знаков, набор которых гораздо шире того, что входит в алфавит естественных языков. Помимо букв, туда точно должны входить музыкальные ноты, символы из математической нотации и бог знает что еще. Свои знаки, вероятно, должны быть не только для отдельных нот и звуков языка, но и для целых музыкальных пассажей и классических цитат — из античных авторов, древнеиндийской и японской поэзии и всего на свете, что доступно интеллектуалам Касталии.
Интерес игры, по всей видимости, в сочетаниях всех этих элементов и стоящей за ними традиции. Скажем, соединение какой-нибудь теоремы с мелодией сонаты ХѴІІ века должно восприниматься как остроумное, а пентатонический ряд с цитатой из Вергилия (пример условный) как нечто скучное и банальное. Некоторой аналогией могут служить правила рифмовки в русской поэзии: глагольная рифма типа «злиться — бриться» скучная, а «товарищи — товар ищи» звучит свежо, потому что каламбур. Кто сказал, что надо воспринимать именно так? Никто, просто сложилась такая традиция, и если ты с этой традицией не знаком, то разницы ты не видишь. Вот почему не понимали игру в бисер люди за пределами Касталии.
В процессе игры все эти сочетания знаков видоизменяются, преобразуются, перетасовываются, образуя новые неожиданные сочетания, а сами преобразования тоже подчиняются каким-то правилам, потому что нет ничего скучнее игры без правил.
Как это переложить в код?
Нужно всего лишь закодировать все культурное наследие человечества и придумать правила преобразования одного состояния разложенного на игровой доске «бисера» в другое. Есть ощущение, что это все-таки ту мач. Начнем с малого.
Что если в наш алфавит пока войдет только китайская письменность и смыслы, которыми мы будем жонглировать, заимствуем из китайской поэзии эпохи Тан? С одной стороны, такое ограничение по сравнению со всей культурой человечества заставляет нас ужаться довольно сильно. С другой стороны, китайский «алфавит» большой, а китайская грамота поэзия — это достаточно заумно, чтобы появился компьютерный прототип игры в бисер.
Правила возьмем из самой статусной интеллектуальной игры — из шахмат. Да, надо было бы из го, но тогда в нашей схеме был бы слишком сильный крен в сторону Востока, а нужно выдерживать диверсификацию стран света.
У китайцев эпохи Тан есть стихи, в которых ровно 8 строк, а иероглифов в строке обязательно 7. В современных изданиях в конце строки ставится знак препинания (,или 。), в результате получаем матрицу 8×8 знаков, что как раз соответствует игровому полю шахмат. Плюс иероглифы выглядят вполне квадратно, что усиливает ассоциации с клетками на доске. С уже существующими танскими стихотворениями и будем работать.
Правила могут быть такими. Берем какую-нибудь реальную шахматную партию и каждый ее ход превращаем в преобразования китайского текста. При ходе фигуры мы будем менять местами иероглифы, которые соответствуют начальному и целевому полю хода. Например, если ладья идет с поля a8 на поле b8, то мы меняем местами первый и второй иероглиф в первой строке. При взятии фигур мы выбрасывать иероглифы не будем, потому что тогда разрушилась бы наша матрица 8×8 и мы не смогли бы играть дальше.
По ходу игры первоначальный поэтический текст будет непредсказуемым образом видоизменяться, а иероглифы вступать в новые неожиданные отношения, что нам и требуется.
Пилим пакет
Берем руководство по созданию питоновских пакетов, отбираем из корпуса танских текстов такие, которые подходят нам по формату 8×8 (8 полноценных иероглифов в 8 строках, увы, не бывает), прикручиваем умение читать формат PGN, в котором записываются листинги шахматных партий — и вот, пакет готов. Роман Гессе называется по-английски «The Glass Bead Game», а у нас пока ограниченный по функциональности прототип, что мы и отразим в названии (тоже каламбурном): «Chess Bead Game».
Пакет умеет подгружать конкретное стихотворение (или случайное, если пользователь не знает, какое стихотворение ему нужно), знает, кто его написал и как оно называется:
import chess_bead as cb
g = cb.Game(poem_num=2000)
Пакет умеет подгружать шахматную партию в формате PGN:
import chess_bead as cb
g = cb.Game(pgn_file='/path/to/file.pgn', poem_num=2000)
Если у пользователя нет под рукой своего PGN-файлика, можно взять из готового набора. Но вообще-то архивы шахматных партий есть циклопические многогигабайтные.
Далее пакет может запустить игру и хранить все состояния «бисера», то есть положения иероглифов после каждого хода.
Вот стихотворение великого поэта Ду Фу до игры:
g = cb.Game(poem_num=1765)
verses = g.start_game()
verses[0].lines
initial_state =[]
for line in verses[0].lines:
initial_state.append(''.join(line))
initial_text = '\n'.join(initial_state)
print(initial_text)
風急天高猿嘯哀,
渚清沙白鳥飛回。
無邊落木蕭蕭下,
不盡長江袞袞來。
萬里悲秋常作客,
百年多病獨登臺。
艱難苦恨繁霜鬢,
潦倒新停濁酒杯。
В переводе Наталии Азаровой:
Взбираясь наверх
ветром нервным взвивается к небу
обезьяний плач по умершим
у отмели белой брызги вращеньем
кругами птиц над водой
так беспрестанно теряют деревья
шуршанье шуршание листьев
так бесконечно катит янцзы
теченье течение встречи
в тысяче вёрст от дома чужой
печальной осенью долгой
годы прожиты болен и одинок
карабкаюсь всё же на башню
тяжко и трудно от горести нелюбви
на висках расцветает иней
странно гляжусь незадачливым старцем
, но больше не пью вина
Другой перевод
Стремителен ветер, и небо высо́ко.
В лесу обезьяны вопят.
Над чистой, осенней водою потока
Осенние птицы летят.
Осенние листья кружат, опадая,
Багряны они и легки,
И тянутся вдаль от родимого края
Просторы Великой реки.
(перевод А. Гитовича)
Теперь перемешаем эти прекрасные стихи, руководствуясь классической партией из матча Спасский-Фишер 1972 года.
g = cb.Game(pgn_file='5152.pgn', chess_game=3, poem_num=1765)
verses = g.start_game()
В конце получилось так:
g.result # А чем закончилась партия?
'1/2-1/2' # ничья
final_state =[]
for line in verses[89].lines:
final_state.append(''.join(line))
final_text = '\n'.join(final_state)
print(final_text)
倒落長潦蕭,霜盡
無來悲蕭停酒木。
不邊杯猿鳥新下,
渚哀苦江臺飛回袞
艱里繁沙袞白天。
萬年難濁急清嘯,
百高客秋常作鬢。
病多獨恨登,風。
Что все это значит? Вот с этим не ко мне. Китайского я не знаю, в Касталии не учился. Могу только воспользоваться онлайн-переводчиком. Он отлично берет на себя функцию оракула:
Мороз закончился
Никто не приходит оплакивать остановку винного дерева.
Чаши обезьян и птиц — новые.
И печальная платформа реки летит обратно в железную
Железо дня на твердом песке.
Миллиона лет недостаточно, чтобы создать свистящий звук.
У ста высоких посетителей осенью часто появляются бакенбарды.
Я болен и ненавижу ветер.
Честно говоря, я не удивлен, что никто не пришел оплакать остановку винного дерева, и про бакенбарды осенью тоже верю. Иными словами, я бы считал, что все сказанное в стихотворении — правда.
Но на буковки без визуализации смотреть скучновато. Прикрутим еще возможность создания картинок и анимации игры. Для этого нам нужен шрифт. Лицензия предполагает, что поставлять его можно только в составе программного обеспечения, что как раз наш случай. Получается так:
v = cb.Viz(verses, g.author, g.title)
v.gif()
Тут поле, на которое ходит фигура, обозначается красным, а поле, с которого она ходит, серым.
Теперь добавим документацию и пакет готов.
Это наш вариант игры в бисер. Разумеется, это частный случай. Нужны другие варианты знаковых систем и другие игры, помимо шахмат, но общее направление теперь различимо.