#game
Как обычно выглядит квест? Персонаж под нашим управлением ходит по локациям, собирает предметы в разных местах и использует их в других местах. Иногда для получения предметов нужно говорить с другими персонажами и выяснять, что им нужно.
А что, если вы... Не понимаете, что вам говорят?
Именно от этого отталкивается игра Chants of Sennaar, вышедшая в сентябре 2023 года.
Как обычно выглядит квест? Персонаж под нашим управлением ходит по локациям, собирает предметы в разных местах и использует их в других местах. Иногда для получения предметов нужно говорить с другими персонажами и выяснять, что им нужно.
А что, если вы... Не понимаете, что вам говорят?
Именно от этого отталкивается игра Chants of Sennaar, вышедшая в сентябре 2023 года.
❤11🤔2
Сенаар (или Шинар) в библейской мифологии — земля, в которой жили люди, начавшие строить Вавилонскую башню. Это название не случайно: всё действие игры происходит внутри исполинской башни, возвышающейся к небесам. Эту башню на разных уровнях населяют несколько различных народов, каждый со своей культурой и, что важно, своим языком.
Каждый язык поначалу абсолютно непонятен. Раскрывать смысл языков приходится косвенным путём — через вывески, фрески, наблюдения за чужими разговорами... Для облегчения этой задачи у главного героя есть при себе тетрадь, в которую он записывает каждое новое слово. К ним можно приписать догадки о возможных значениях слов, и тогда каждый раз при наведении курсора они будут подсвечиваться под соответствующими символами.
К счастью, гадать всю игру о смыслах не обязательно — тут есть помогающая механика (похожая на аналогичную из Return of the Obra Dinn). Каждый раз, когда игрок собирает пачку символов из одного языка, главный герой зарисовывает в тетради страницу, иллюстрирующую различные понятия. Если перетащить в слоты рядом с ними корректные символы из словаря, верно обозначающие понятия, то игра закрепляет их на месте и выдаёт напрямую истинные значения. После этого для предложений, которые состоят полностью из известных символов, будут в подсказках корректно переведены на язык, на котором вы играете (русскоязычная локализация тоже есть). Почивать на лаврах, впрочем, не придётся — у каждого народа полностью свой язык, отличающийся и лексикой, и грамматикой. При этом некоторые слова есть не во всех языках.
Ощущения от того, что ты смог только что сложить смыслы, о которых ты имеешь лишь смутное подозрение, и получить корректный ответ — невероятны. Да, номинально это в основном пазл, но они обычно не дают столь глубокого чувства удовлетворения. Разработчикам удалось достаточно хорошо выдержать баланс сложности — всю игру вполне реально пройти, полагаясь исключительно на свои мозги. Судя по ачивкам в Steam, игру прошли 55% купивших её игроков — и это, если что, прям дохрена.
Выглядит игра прекрасно — в картинке используются чистые тона, местами градиенты и полоски вместо затенения, а сами объекты на локациях имеют обводку. У каждого яруса башни своя собственная палитра, позволяющая на глаз прикинуть, где расположена локация.
Более подробно об игре рассказывается в этом видео от Game maker's toolkit, но там немного больше спойлеров. В частности, там для иллюстрации показывается решение самой первой загадки в игре.
Разумеется, обзор был бы неполным без упоминания недостатков.
Самый явный: так как игра никак не ограничивает подстановку символов, абсолютно ничто не мешает игроку подобрать нужные смыслы тупым перебором и самолично лишить себя львиной доли удовольствия от игры. К сожалению, тут у геймдизайнеров нет хороших средств — никто не может помешать игроку играть неинтересно.
Пожалуй, второй по значимости недостаток — неоднозначности, естественным образом вытекающие из использования иллюстраций. Иногда открытые истинные значения слов заметно отличаются от тех, которые были у тебя на уме. Для сохранения атмосферы можно не пользоваться внутриигровой тетрадью, но это существенно повысит сложность игры. Сама тетрадь, кстати, записывает символы по порядку их получения, из-за чего близкие по значению символы могут быть разбросаны по разным местам страницы.
Сами языки в игре довольно просты:в них нет окончаний, падежей, конкретных указаний на время и на отношения в сложноподчинённых предложениях. Также один символ — это почти всегда одно слово . Более того, персонажи в игре используют одни и те же символы и для письменной, и для устной речи, что портит погружение, если подумать над этим хотя бы пару секунд. С другой стороны, это оправдано гейм-дизайном: приближение сложности языков к реальным резко повысило бы сложность игры, что сделало бы её значительно менее приятной.
Лингвистические пазлы прекрасны, но иногда игра решает изобразить стелс. Это несколько выбивает из темпа и довольно плохо вяжется с остальной игрой, даже если это и худо-бедно обосновано сюжетно.
Каждый язык поначалу абсолютно непонятен. Раскрывать смысл языков приходится косвенным путём — через вывески, фрески, наблюдения за чужими разговорами... Для облегчения этой задачи у главного героя есть при себе тетрадь, в которую он записывает каждое новое слово. К ним можно приписать догадки о возможных значениях слов, и тогда каждый раз при наведении курсора они будут подсвечиваться под соответствующими символами.
К счастью, гадать всю игру о смыслах не обязательно — тут есть помогающая механика (похожая на аналогичную из Return of the Obra Dinn). Каждый раз, когда игрок собирает пачку символов из одного языка, главный герой зарисовывает в тетради страницу, иллюстрирующую различные понятия. Если перетащить в слоты рядом с ними корректные символы из словаря, верно обозначающие понятия, то игра закрепляет их на месте и выдаёт напрямую истинные значения. После этого для предложений, которые состоят полностью из известных символов, будут в подсказках корректно переведены на язык, на котором вы играете (русскоязычная локализация тоже есть). Почивать на лаврах, впрочем, не придётся — у каждого народа полностью свой язык, отличающийся и лексикой, и грамматикой. При этом некоторые слова есть не во всех языках.
Ощущения от того, что ты смог только что сложить смыслы, о которых ты имеешь лишь смутное подозрение, и получить корректный ответ — невероятны. Да, номинально это в основном пазл, но они обычно не дают столь глубокого чувства удовлетворения. Разработчикам удалось достаточно хорошо выдержать баланс сложности — всю игру вполне реально пройти, полагаясь исключительно на свои мозги. Судя по ачивкам в Steam, игру прошли 55% купивших её игроков — и это, если что, прям дохрена.
Выглядит игра прекрасно — в картинке используются чистые тона, местами градиенты и полоски вместо затенения, а сами объекты на локациях имеют обводку. У каждого яруса башни своя собственная палитра, позволяющая на глаз прикинуть, где расположена локация.
Более подробно об игре рассказывается в этом видео от Game maker's toolkit, но там немного больше спойлеров. В частности, там для иллюстрации показывается решение самой первой загадки в игре.
Разумеется, обзор был бы неполным без упоминания недостатков.
Самый явный: так как игра никак не ограничивает подстановку символов, абсолютно ничто не мешает игроку подобрать нужные смыслы тупым перебором и самолично лишить себя львиной доли удовольствия от игры. К сожалению, тут у геймдизайнеров нет хороших средств — никто не может помешать игроку играть неинтересно.
Пожалуй, второй по значимости недостаток — неоднозначности, естественным образом вытекающие из использования иллюстраций. Иногда открытые истинные значения слов заметно отличаются от тех, которые были у тебя на уме. Для сохранения атмосферы можно не пользоваться внутриигровой тетрадью, но это существенно повысит сложность игры. Сама тетрадь, кстати, записывает символы по порядку их получения, из-за чего близкие по значению символы могут быть разбросаны по разным местам страницы.
Сами языки в игре довольно просты:
Лингвистические пазлы прекрасны, но иногда игра решает изобразить стелс. Это несколько выбивает из темпа и довольно плохо вяжется с остальной игрой, даже если это и худо-бедно обосновано сюжетно.
🔥7❤5👍1
Сами задачи, которые надо решать, иногда вызывают раздражение из-за количества бектрекинга — особенно ближе к концу и особенно, если пытаться взять необязательные побочные ачивки. Это несколько перекрывается ограниченной возможностью быстрого перемещения, но карты в игре нет, так что ориентироваться придётся на собственную память.
Ну и, разумеется, как и все квесты и пазлы, игра одноразовая: проходить заново имеет смысл разве что для сбора ачивок.
Существенны ли эти недостатки? Я бы сказал, что нет (кроме первого), так что всячески рекомендую Chants of Sennaar. Благо, она достаточно небольшая, чтобы её при желании можно было пройти за пару-тройку вечеров.
Ну и, разумеется, как и все квесты и пазлы, игра одноразовая: проходить заново имеет смысл разве что для сбора ачивок.
Существенны ли эти недостатки? Я бы сказал, что нет (кроме первого), так что всячески рекомендую Chants of Sennaar. Благо, она достаточно небольшая, чтобы её при желании можно было пройти за пару-тройку вечеров.
YouTube
A puzzle game where you decipher languages
🔴 Get my premium monthly newsletter - https://gamemakerstoolkit.com/digest/ 🔴
Chants of Sennaar is an innovative puzzle game about deciphering cryptic languages. It's the first entry in a new series of indie game recommendations called "Weekender".
===…
Chants of Sennaar is an innovative puzzle game about deciphering cryptic languages. It's the first entry in a new series of indie game recommendations called "Weekender".
===…
❤🔥1👍1
Игнорируйте всё, что я сказал выше, в Chants of Sennaar можно погладить кошку, всё, это игра десятилетия.
fxtwitter.com/CanYouPetTheDog/status/1704198156940943558
fxtwitter.com/CanYouPetTheDog/status/1704198156940943558
🧵 Thread • FxTwitter / FixupX
Can You Pet the Dog? (@CanYouPetTheDog)
You can pet the cat in Chants of Sennaar
🥰9❤7
Forwarded from Pragmatic Programmer (Nikita Bishōnen)
Итак друзья, вопрос к вам (но только тем, кто не пишет на Rust), как вы в своём языке напишите код, аналогичный следующему?
Разъясню что он делает. В случае, если у нас не получилось сконвертировать метаданные коллекции и её типажи, мы не делаем ничего. Если же оперция прошла успешно, мы добавляем её результат в коллекцию.
if let Ok(collection) = Collection::try_from((collection_metadata, traits)) {
result.push(collection);
}
Разъясню что он делает. В случае, если у нас не получилось сконвертировать метаданные коллекции и её типажи, мы не делаем ничего. Если же оперция прошла успешно, мы добавляем её результат в коллекцию.
🤔3
Forwarded from Pragmatic Programmer (Nikita Bishōnen)
Я противник того, чтобы доходить до максимума в вопросах копирования кода. Неверная абстракция обычно намного дороже чем копи паста. Однако есть моменты, которые хочется оптимизировать. Например трассировка вызовов методов и их инструментация.
Для этого я пользуюсь instrument атрибутом от tracing библиотеки. Каждая важная функция имеет плашечкуинагента :
Но есть проблема, каждый раз писать схожие аннотации, вспоминая точный синтаксис, это бывает утомительно и точно непродуктивно. Как же я был сегодня рад найти молодую утилиту для моего #toolkit — attr_alias.
Как же её готовить? Создавайте файл
Далее — идём в код и используем его:
Вуаля, теперь я могу использовать маленький алиас вместо грузного объявления всего, что мне нужно.
А ещё у этой утилиты нет зависимостей, кроме стандартной библиотеки Rust. Очень котирую и всем советую. Дай бог поменьше зависимостей, да будут мои билды быстрыми, аминь 🧎🤲
Для этого я пользуюсь instrument атрибутом от tracing библиотеки. Каждая важная функция имеет плашечку
#[instrument(err(Debug), skip(executor), ret, level = "trace")]
fn insert(...) -> Result<...> { ...
Но есть проблема, каждый раз писать схожие аннотации, вспоминая точный синтаксис, это бывает утомительно и точно непродуктивно. Как же я был сегодня рад найти молодую утилиту для моего #toolkit — attr_alias.
Как же её готовить? Создавайте файл
src/attr-aliases.txt
(даже если у вас workspace — я думаю скоро я или автор поправим это) и пишите в нём алиас, я написал: *db_instrument=instrument(err(Debug), skip(executor), ret, level = "trace")
Далее — идём в код и используем его:
#[attr_alias(db_instrument)]
pub async fn select_...<'a, E>(...: &str, executor: E) -> Result<...>
Вуаля, теперь я могу использовать маленький алиас вместо грузного объявления всего, что мне нужно.
А ещё у этой утилиты нет зависимостей, кроме стандартной библиотеки Rust. Очень котирую и всем советую. Дай бог поменьше зависимостей, да будут мои билды быстрыми, аминь 🧎🤲
docs.rs
instrument in tracing_attributes - Rust
Instruments a function to create and enter a `tracing` span every time the function is called.
🔥5🤔5🤮1
Forwarded from I’m CTO, bitch
Вопрос «Что происходит, когда пользователь вводит URL в браузере?» мы на собеседованиях больше НЕ задаём.
Кандидат сегодня был очень напористый. Он поставил себе цель рассказать вообще всё!
За первые 20 минут он рассказал, как сигналы с клавиатуры передаются в операционную систему и как происходит парсинг url в браузерах. Я пытался его остановить, но он всё же прочитал небольшую лекцию по модели OSI. И даже для наглядности обжал витую пару прямо во время собеседования. Ещё час он рассказывал про устройство DNS и детали работы WiFi сетей...
Собеседование закончилось 3 часа назад. А он продолжает писать нам на почту: про SSL/TLS Handshake, DOM, рендеринг, кеш, CSSOM, алгоритмы сжатия.
#найм
Кандидат сегодня был очень напористый. Он поставил себе цель рассказать вообще всё!
За первые 20 минут он рассказал, как сигналы с клавиатуры передаются в операционную систему и как происходит парсинг url в браузерах. Я пытался его остановить, но он всё же прочитал небольшую лекцию по модели OSI. И даже для наглядности обжал витую пару прямо во время собеседования. Ещё час он рассказывал про устройство DNS и детали работы WiFi сетей...
Собеседование закончилось 3 часа назад. А он продолжает писать нам на почту: про SSL/TLS Handshake, DOM, рендеринг, кеш, CSSOM, алгоритмы сжатия.
#найм
😁43🤷3❤🔥2🤩2🔥1
#prog #article
Memory Unsafety is an Attitude Problem
<...>
That’s not to say memory safety isn’t important, it is. But lacking security is ultimately an attitude problem. If developers don’t care about it, forcing onto them a memory safe language will not accomplish anything. They’ll screw up some other way.
First make them care, then make it easy for them to do the right thing.
Впрочем, попенять статью можно за методику для подкрепления некоторых аргументов (комментарии на реддите? Серьёзно?)
Memory Unsafety is an Attitude Problem
<...>
That’s not to say memory safety isn’t important, it is. But lacking security is ultimately an attitude problem. If developers don’t care about it, forcing onto them a memory safe language will not accomplish anything. They’ll screw up some other way.
First make them care, then make it easy for them to do the right thing.
Впрочем, попенять статью можно за методику для подкрепления некоторых аргументов (комментарии на реддите? Серьёзно?)
👍7🤨3
#prog #cpp
В стандартной библиотеке C++ есть unordered контейнеры, которые для проверки принадлежности элементов контейнеру используют хэш-функции (помимо равенства, разумеется). Для того, чтобы хэшировать значение, нужно знать, как это делается.
В C++ операции хеширования можно переопределять для конкретных контейнеров (это один из шаблонных параметров), но по умолчанию используется std::hash. Для того, чтобы определить операцию хеширования для своего типа, нужно написать специализацию
Пусть у нас вот такой простой тип:
Попробуем сделать его хешируемым:
И вот тут мы сталкиваемся с проблемой: при условии, что у нас все поля хэшируемые, как нам получить хэш от всех них? Увы, std тут вообще никак не помогает. Максимум, что могут предложить на просторах интернета — это использовать boost::hash_combine (это даже рекомендуют на cppreference.com). Мало того, что тащить буст ради этого не хочется, так ещё и комбинирование происходит на уровне готовых хэшей. Это фактически приводит к двойному хэшированию, что обычно на качестве хэш-функции сказывается не в лучшую сторону.
В теории можно было бы организовать разделение на саму хэш-функцию и на описание того, какие и в каком порядке поля типа хэшируются... То есть сделать так, как в Rust. И это даже предлагали сделать для C++ в предложении N3980 aka Types Don't Know #. И подано это предложение было... 24 мая 2014 года. То есть десять лет назад, да. А воз и ныне там.
В стандартной библиотеке C++ есть unordered контейнеры, которые для проверки принадлежности элементов контейнеру используют хэш-функции (помимо равенства, разумеется). Для того, чтобы хэшировать значение, нужно знать, как это делается.
В C++ операции хеширования можно переопределять для конкретных контейнеров (это один из шаблонных параметров), но по умолчанию используется std::hash. Для того, чтобы определить операцию хеширования для своего типа, нужно написать специализацию
std::hash
для своего типа (обязательно в пространстве имён std) и написать свою реализацию operator()
, которая будет принимать хэшируемый объект и возвращать std::size_t
в качестве результата.Пусть у нас вот такой простой тип:
struct Point {
int x;
int y;
};
Попробуем сделать его хешируемым:
#include <cstddef>
#include <functional>
template <> struct std::hash<Point> {
std::size_t operator()(const Point& p) {
// а как...
}
};
И вот тут мы сталкиваемся с проблемой: при условии, что у нас все поля хэшируемые, как нам получить хэш от всех них? Увы, std тут вообще никак не помогает. Максимум, что могут предложить на просторах интернета — это использовать boost::hash_combine (это даже рекомендуют на cppreference.com). Мало того, что тащить буст ради этого не хочется, так ещё и комбинирование происходит на уровне готовых хэшей. Это фактически приводит к двойному хэшированию, что обычно на качестве хэш-функции сказывается не в лучшую сторону.
В теории можно было бы организовать разделение на саму хэш-функцию и на описание того, какие и в каком порядке поля типа хэшируются... То есть сделать так, как в Rust. И это даже предлагали сделать для C++ в предложении N3980 aka Types Don't Know #. И подано это предложение было... 24 мая 2014 года. То есть десять лет назад, да. А воз и ныне там.
Arthur O’Dwyer
Why can’t I specialize std::hash inside my own namespace?
This question comes up a lot on the cpplang Slack.
Suppose I have a class named my::Book, and I want to put it into a std::unordered_set.
Then I need to write a std::hash specialization for it. So I write:
Suppose I have a class named my::Book, and I want to put it into a std::unordered_set.
Then I need to write a std::hash specialization for it. So I write:
🤡9🤷6🤔3🤣3