• Как сделать управляемый интерфейс?

    @MarkusD
    все время мелю чепуху :)
    Макар Герасимов , такс, для начала отключись от рисования и восприятия процесса глазами.

    Первое, что тебе надо - это модель игрового поля. Модель - от слова "данных". В первую очередь тебе надо представить игру в виде модели данных, а не визуального представления. Вот давай пробуем...

    Тетрадный лист в клетку напоминает что? - правильно, двухмерный массив. Стало быть, игровое поле удобно представить в виде двухмерного массива. А какой тип ячейки массива? - раз у нас игра про слова, которые по буквам пишутся в клетках, то, пожалуй, в ячейках надо хранить символы.

    Крутяк, придумали игровое поле, в котором хранятся символы! Только заметь, символа в ячейке может и не быть.

    Хорошо, идем дальше. У нас есть два игрока, которые могут писать слова.
    Ага... Если писать, то надо знать - куда. Пользователю нужно указание, куда он сейчас будет писать - указать ячейку, в которую он сейчас поставит символ. А ячейки у нас в двухмерном массиве. Удобнее всего будет хранить координаты текущей ячейки пользователя!

    Стало быть, пользователь у нас, как минимум, определяется координатами выбранной им ячейки на поле.

    Каждому пользователю отведен ход, за время хода пользователь может менять позицию его выбранной ячейки и пробовать ввести слово. Стало быть, по событиям от клавиш можно управлять только выделенной ячейкой текущего пользователя. При этом надо не забывать ограничивать управление размерами игрового поля.

    Когда пользователь вводит слово, нужно ограничивать движение его выделения только соседними клетками. Поставил пользователь первый символ, все, он вводит слово.

    И заметь, это все полностью отвязано от графического представления. Сперва всегда надо продумывать именно модель данных.
    На данном этапе описания у нас есть поле, пользователи с их выделениями и введенные слова. Модель данных дает полное представление как ее представлять пользователю.

    Ну разве проблема отрисовать двухмерный массив символов? А по координатам закрасить другим цветом выделенную пользователем ячейку?

    Как позволить пользователю выделять слова? После того как пользователь ввел свою букву, ему можно позволить перемещаться в ячейки с уже введенными буквами и маркировать их как слово. Введенное слово можно или по словарю проверить, или попросить подтвердить правильность слова всеми пользователями.

    Когда твоя модель данных игры будет хоть немного готова, ее можно будет без труда хоть в консоль рисовать, хоть на поверхности окна.
    Модель - это набор классов со связями. Поле, игра, игрок, ход, слово, словарь, таблица введенных слов... символ слова(?). Полный список сущностей ты знаешь лучше меня. Просто думай на уровне данных. Тогда и управлять ими сможешь. :)
    Ответ написан
    4 комментария
  • Как включить поддержку C11?

    @MarkusD
    все время мелю чепуху :)
    Ответ написан
    Комментировать
  • Как устроен игровой протокол передачи данных?

    @MarkusD
    все время мелю чепуху :)
    https://habrahabr.ru/post/209144/
    https://habrahabr.ru/post/209524/
    https://habrahabr.ru/post/213559/

    Оригиналы:
    web.archive.org/web/20190328001900/https://gaffero...

    Дополнительные материалы:
    web.archive.org/web/20190328001900/https://gaffero...

    По этому поводу, в целом, советую почитать блог Гленна Фидлера.

    Так же есть удобные библиотеки, которые сами собой отвечают на остальные твои вопросы:
    https://developers.google.com/protocol-buffers/
    https://capnproto.org/
    Ответ написан
    5 комментариев
  • Как разделить жесткий диск на 2 раздела и сделать их с разными файловыми системами?

    @MarkusD
    все время мелю чепуху :)
    Для этих целей есть ведь гора утилит. Partition Magic и Acronis Disk Director к примеру.
    Первым я пользовался в нулевых, последним - и по сей день.
    Ответ написан
    3 комментария
  • Для чего нужен singleton?

    @MarkusD
    все время мелю чепуху :)
    В буквальном смысле синглтон призван упростить владение, инициализацию, контроль времени жизни и доступ к элементам глобального состояния программы. Эта фраза является самой важной. А важнее всего - правильно понять ее.

    Подход многократно обличался антипаттерном за то, что "глобальное состояние программы - это зло", не смотря на то, что глобальное состояние продолжает оставаться в программе даже после удаления всех синглтонов из проекта.
    Я видел проекты с более чем 50 синглтонами и очень тяжелыми проблемами их связи между собой. В синглтоны без разбора и по незнанке вытаскивали буквально все. Это яркий пример антиподхода применения абсолютно любого паттерна.
    Важно понимать что никакой элемент проектирования не является антипаттерном, он приводит к проблемам только при неумелом использовании.

    Приложение состоящее ровно из одной UI формы будет иметь глобальное состояние в виде этой формы вне зависимости от того, будет ли эта форма оформлена как синглтон или нет. Просто, если нет, то этой формой приходится управлять иными путями, не всегда более удобными чем синглтон.

    В качестве альтернативы синглтонам пропагандируются два других подхода: Registry / Service locator и Dependency injection.
    К слову, все альтернативы столь же активно и все так же незаслуженно обзываются антипаттернами. :)

    Теперь к примерам.
    - DirectX. Для работы с графикой тебе необходимо по одной инстанции интерфейсов IDirect3D и IDirect3DDevice. Эти две инстанции декларируют глобальное состояние программы. Инстанции всех буферов, текстур, шейдеров и поверхностей создаются с использованием этих инстанций. Разумным будет предоставить доступ к инстанциям DirectX через подход синглтона.
    - OpenGL старых добрых версий. Процедурный интерфейс OpenGL как бы намекает на отсутствие необходимости в глобальном состоянии. Но не тут то было. Для работы с OpenGL необходимо не просто создать контекст, но еще и помнить поток, в котором этот контекст связан с поверхностью вывода. В многопоточной среде контекстов может быть несколько для параллельной загрузки ресурсов. В этом случае помнить надо уже два потока и два контекста (минимум). Само собой, в синглтоне это глобальное состояние смотрится удобнее.
    - Sockets. Не важно какие. Когда твое приложение представляет собой MMO проект и у тебя гора подсистем, постоянно и обособленно общающихся с сервером, сетевое подключение разумно оформить в виде синглтона.
    - Assets/Resources - они бывают разные, кешируемые и нет, доступные из сети, с жесткого диска, из подсистемы пререндеринга. Опять же, я несколько раз видел боль и страдания от неоднородного контроля ресурсов без соответствующей подсистемы. А сама подсистема управления ресурсами всегда централизована и лучше всего реализуется именно на синглтоне.

    Звук, ввод, многопоточный конвеер задач... вспоминать места явной необходимости синглтонов можно еще долго.
    Ответ написан
    Комментировать
  • Как в C++ скрыть определение вспомогательных типов?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    В целом, твой вопрос не имеет точного ответа, т.к. вопрос затрагивает темы вкуса цветных карандашей и просто является риторическим.

    Помимо всей этой воды, что я ниже изложил, еще очень стоит ознакомиться с разделом "SF: Source files" официального гайдлайна:
    https://github.com/isocpp/CppCoreGuidelines/blob/m...
    (Очень советую изучить весь гайдлайн от корки до корки)

    По первому вопросу, коротко: если тип используется по значению - полное объявление типа обязательно, иначе можно обойтись Forward declaration.

    Немного подробнее:

    Существует такой принцип формирования проекта, когда каждый заголовочный файл предоставляет полную информацию о зависимостях. Forward declaration в таком случае или запрещено, или сильно порицается. Каждый заголовочный файл должен обязательно включать в себя заголовочные файлы всех зависимостей. А файл исходного кода должен включать ровно один заголовок - тот, чей интерфейс реализуется в исходном коде.
    Это позволяет сразу видеть все зависимости кода, не париться с размещением файлов, не париться с транзитивными зависимостями, просто не париться, а так же существенно огребать на времени компиляции. Особенно если в проекте разрешен только #include "", а #include <> порицается.
    В качестве примера можно почитать UE4.

    Существует и другой принцип формирования проекта, когда файловая структура отражает макроуровень архитектуры проекта. Скажем, если проект базируется на подсистемах или слоях, каждый слой(подсистема) представляется ровно одним заголовочным мастер-файлом и своей отдельной папкой в файловой системе. Внутри этой папки расположены все внутренние заголовочные файлы подсистемы. Ни один внутренний заголовочный файл не содержит никаких зависимостей, только объявление своего интерфейса. Все зависимости (FD, другие подсистемы или слои, системные заголовки и.т.д.) описывает только мастер-файл, он же в нужной последовательности подключает все внутренние заголовки. Другие подсистемы, равно как и исходный код самой подсистемы, включают в себя только мастер-файл.
    Это позволяет снять всю шелуху с внутренних заголовков и сосредоточить их текст ровно на декларации интерфейса. С другой стороны, ради использования какого-нибудь мелкого типчика всегда приходится подключать всю подсистему, т.к. внутренние заголовки подключать нельзя по правилам формирования проекта и потому что их зависимости неясны.

    В обоих принципах применяется одно очень важное правило: Один класс - один комплект исходного кода с именем самого класса. Это, можно сказать, самый базовый принцип формирования проектов. Классы с инвариантом и богатым функционалом должны быть объявлены каждый в своем отдельном заголовке, имеющим имя класса. Описание функционала каждого такого класса должно лежать в своем отдельном файле исходного кода с именем описываемого класса. Иногда и вовсе требуется несколько файлов исходного кода на один класс, потому что класс выполняет слишком много функций, но разделять его нельзя.
    Этот принцип нередко приводит к проблеме циклической зависимости, когда два класса ссылаются друг на друга и в каждом заголовке необходимо включение второго заголовка. В этом случае помогает или редизайн классов для ослабления зависимостей, или Forward declaration как меньшее из зол.

    С точки зрения компилятора есть только один формат файла - формат исходного кода, который ему и надо обработать.
    С точки зрения человека форматов файла не два, а 3 или 4:
    • .c , .cc , .cxx , .cpp , c++ - формат исходного кода, в котором стоит производить определение интерфейсов и держать все приватные инструменты (код и типы);
    • .h , .hh , .hpp - формат заголовка, в котором подключаются заголовки зависимостей и объявляется интерфейс - ровно то, что может понадобиться в другом коде или не может быть определено в файле исходного кода. И ничего больше;
    • .inl - формат вспомогательного заголовка, в котором производится определение inline функций и сложных шаблонных конструкций;
    • .inc - формат вспомогательного заголовка, в котором описываются форварды, внешние глобальные переменные, константы и прочие данные. Этот формат используется реже всего. Вместо него чаще используют формат заголовка (.h), размещая в нем весь контент .inc файла.


    Если с "человеческими форматами" все должно быть хорошо понятно, то с форматом файла для компилятора стоит уяснить одну тонкость - все эти человеческие шахматы с бубнами и делением на файлы должны складываться в как можно более удобный для компиляции вид. Чем меньше одинаковых #include, тем лучше. Чем меньше #include в целом, тем лучше. Трансляция - дело итак нелегкое.
    Ответ написан
    Комментировать
  • Можно ли так инициировать компоненты класса?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    en.cppreference.com/w/cpp/language/class
    Параграф "Member specification".
    Если ты используешь стандарт c++11 и выше, то инициализация полей непреложна.

    Единственным исключением будет список инициализации полей в пользовательском конструкторе.
    en.cppreference.com/w/cpp/language/initializer_list
    Ответ написан
    Комментировать
  • Всегда ли в C++ false == 0, true == 1?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    В стандарте по этому поводу все однозначно.
    eelis.net/c++draft/conv.prom#6
    Ответ написан
    3 комментария
  • Как с помощью constexpr C++11 создать const объект, который бы содержал внутри себя массив (const) указателей на объекты другого класса?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Суть вопроса немного туманна. Полностью понять, что тебе нужно, не получается.

    Есть техника статической цепочки, ее суть сводится к тому, чтобы на этапе компиляции создать односвязный список объектов, к которым в рантайме требудется получать доступ. Эта техника хорошо реализована в DOOM3 для декларации всех опций движка.
    Корень статической цепочки в думе находится вот тут: https://github.com/TTimo/doom3.gpl/blob/master/neo...

    А регистрация опций делается так:
    https://github.com/TTimo/doom3.gpl/blob/master/neo...

    Как видишь, статическая цепочка нужна больше для хранения экземпляров общего интерфейса.
    А если тебе надо просто собрать группу статически инициализированных объектов одного типа, то сойти может и такое решение:
    cpp.sh/8eco
    Ответ написан
    Комментировать
  • Почему неправильно считывается системное время?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    https://msdn.microsoft.com/en-us/library/1f4c8f33.aspx

    Все дело в том, что функция time() возвращает UTC метку времени. То есть, время по Гринвичу.
    Тебе надо сделать корректировку на свой часовой пояс для получения локального времени.

    Посмотри пример по этой ссылке:
    en.cppreference.com/w/cpp/chrono/c/gmtime
    Ответ написан
    Комментировать
  • Как добавить поддержку ZLIB в Android Studio ( C++ )?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    cannot find -llz

    Память мне подсказывает что не "-llz", а "-lz". :)
    Ответ написан
  • Как исправить ошибку 'сбой при специализации функции-шаблона' (многопоточность)?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    en.cppreference.com/w/cpp/thread/thread/thread

    int run(); // Имеет тип `int (FirstClass::*)()`. А конструктору `std::thread` нужен или функтор, или глобальная функция.


    Перепиши `run()` на статическую и передавай инстанцию объекта `FirstClass` параметром при запуске.
    иное решение - использовать лямбду с замыканием на this.
    FirstClass::FirstClass() : /* список инициализаторов */ firstClassThread([this](){ run(); }) { }


    Имей в виду что поток запустится сразу же после завершения конструктора `std::thread`.

    en.cppreference.com/w/cpp/thread/thread
    Threads begin execution immediately upon construction of the associated thread object (pending any OS scheduling delays), starting at the top-level function provided as a constructor argument.

    Буквально, поток запускается сразу после его конструирования.

    en.cppreference.com/w/cpp/thread/thread/detach
    Separates the thread of execution from the thread object, allowing execution to continue independently.

    А вот `detach` ничего с запуском потока, или с самим потоком, не делает, она только сбрасывает состояние объекта потока, обрывает связь между объектом потока и самим потоком.

    Может тебе стоит как-то иначе запускать поток, в другое время? Если тебе все таки надо запустить поток в другое время, его в это время и надо конструировать.
    Ответ написан
    2 комментария
  • Есть ли в stl такой мьютекс, который можно разблокировать из другого потока?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Если тебе надо заблокировать один поток (или несколько, поставив их в очередь блокировки) до события в другом потоке, то тебе хорошо подойдет std::condition_variable.

    На основе этой сущности можно реализовать механику "wait() -> notify/notifyall()" из pthread_monitor или из Java.
    Ответ написан
    2 комментария
  • Как исправить ошибку 'map итератор неразыменовываемый'?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    if ((!words.empty() && words.find(word) != words.end() && word_str != words.end()->first) || std::string(word) == words.end()->first)

    Условие страшное, отвыкай так писать. Разделяй на поясняющие переменные и группируй проверки скобками.

    А проблема в words.end()->first.
    en.cppreference.com/w/cpp/container/map/end
    Итератор, возвращаемый end() не может быть разыменован.

    UPD:
    Задача, как я вижу, собеседовательная. У нее есть более простое и быстрое решение с меньшим количеством строк.
    Ответ написан
    4 комментария
  • Какой смысл в фильтрах Visual Studio?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Фильтры в Visual C++ - это методика организации рабочего пространства. Это старая и проверенная методика, зарекомендовавшая себя в MSVC++ еще с очень старых времен.
    Система фильтров позволяет организовать любую лапшу из файлов сторонней библиотеки, в которой все сделано по принципу: "Работает == Не трогай". Этим она чрезвычайно полезна.
    Так же фильтры позволяют сделать более тонкую организацию в тех местах, где на уровне файловой структуры она бессмысленна.

    Я бы не был так поспешен, ругая инструмент за то, что им пользуются явно неорганизованные или неграмотные "специалисты". В подавляющем большинстве проектов ты просто видел невежество. И ничего больше.
    Да, фильтры никак не привязаны к реальным папкам, а это и не надо. Да, на диске все можно хранить в одной папке (и в этом реальный плюс фильтров). Да, фильтры позволяют организоваться только внутри пространства самой среды.
    Но и ничего больше.
    Многие люди не считают должным разделять заголовки и исходный код по разным папкам, пеняя на "слишком сложную" организацию Include Directories. Только это не проблема среды, это такой у людей стиль организации рабочего пространства.
    Ответ написан
    3 комментария
  • Генерация 2d ландшафта в unity Как реализовать?

    @MarkusD
    все время мелю чепуху :)
    Какое благородное у тебя занятие. Похвально будет если дети вырастут настоящими разработчиками игр. :)

    А на счет генерации - смотри в сторону алгоритма Square-Diamond. Мне кажется, это именно то, что тебе поможет.

    Ссылки по теме:
    https://habrahabr.ru/post/111538/
    https://habrahabr.ru/post/249027/
    https://habrahabr.ru/post/226635/ - на 3D-шность не смотри, это в целом поможет тебе вникнуть в механики.
    https://habrahabr.ru/post/128368/

    А еще вот тут мой хороший знакомый и бывший коллега соорудил маленькую поделку. Возможно, это тоже будет тебе полезно. :)
    www.gamedev.ru/projects/forum/?id=181167

    Обращайся за дополнениями.
    Ответ написан
    1 комментарий
  • Как получить изображение максимально малого объёма для микроконтроллеров?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Для максимального сжатия изображений используют палитровую графику. Суть в том, чтобы в пиксельной матрице изображения заменить тяжелую информацию о цвете на значительно более легкий индекс цвета в некоторой цветовой палитре.

    Для картинки считается количество уникальных цветов, потом все уникальные цвета складываются в палитру и индексируются, а матрица цветов заменяется на матрицу индексов цветов.
    Число цветов в палитре принято делать от степени двойки, чаще всего встречаются палитры в 256 цветов. В этом случае размер индекса цвета в матрице занимает 1 байт. 16 цветов в палитре - индекс уже 4 байта. и.т.д.
    Прозрачность в этом случае неудобств не доставляет, т.к. в палитре можно спокойно хранить цвет в формате RGBA.

    Когда число цветов нужно вписать в некоторые рамки, для этого применяют разные алгоритмы так называемого dithering.
    Ответ написан
    Комментировать
  • Как реализовать частичную специализацию для указателя на определённый класс?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    В общем, Антон Жилин все описал правильно, но есть еще один выход.

    Чтобы понять преимущество предлагаемого мной подхода, нужно обратиться к коду std::basic_string.
    Например, вот эта реализация.
    https://android.googlesource.com/platform/ndk/+/bf...

    Вторым параметром идут так называемые черты - traits для типа символов в строке. По своей сути функциональность std::string, std::wstring, std::u16string и std::u32string отличается только реализацией именно этого параметра.
    Сам шаблон черт для строки описан немногим выше 75й строки.
    Вся реализация std::basic_string полностью опирается на список статических функций из заданных в шаблоне черт.

    Я предлагаю вынести частную специализацию за пределы твоего шаблона List и определить черты ListTraits, которые уже и специализировать для нужных классов, в частности - для Storage_device.
    В черты можно и нужно выделять только важные функции. Это можно сравнить с вынужденной мерой.

    template< typename TDataType >
    class ListTraits
    {
    public:
    	// ...
    	
    	template< typename TNodeType >
    	inline static void AddHead( TNodeType node, TDataType data );
    	
    	// ...
    };
    
    template<>
    class ListTraits<Storage_device*>
    {
    public:
    	// ...
    	
    	template< typename TNodeType >
    	inline static void AddHead( TNodeType node, Storage_device* data );
    	
    	// ...
    };
    
    template< typename TNodeType, typename TDataType >
    class List
    {
    	// Тип используемых черт.
    	using Traits = ListTraits<TDataType>;
    	
    	TNodeType *head, *tail;
    	int count;
    
    public:
    	// ...
    	
    	void AddHead(TDataType data);
    	
    	// ...
    };
    
    template< typename TNodeType, typename TDataType >
    void List<TNodeType, TDataType>::AddHead( TDataType data )
    {
    	// ...
    	
    	Traits::template AddHead( temp, data );
    	
    	// ...
    };
    Ответ написан
    Комментировать
  • Что происходит с переменной / указателем в памяти после его обнуления?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Смотрю, на этот вопрос все еще нет ответов, поэтому добавлю немного отсебятины.

    Что произойдет после данной вышеописанной операцией с членом size и указателем старого объекта (other)? Они останутся в памяти вместе с объектом? Или при нулевом значение они занимают незначительную область памяти?


    Давай обратимся к C++ Core Guidelines за разъяснениями.
    Советую, кстати, детально изучить от корки до корки.

    https://github.com/isocpp/CppCoreGuidelines/blob/m...
    В результате перемещения источник должен оставаться в состоянии, когда им можно дальше пользоваться.

    Наш объект класса SomeClass может быть выделен как на стеке, так и в куче. Когда мы его перемещаем, мы перемещаем только внутренности объекта, а не сам объект. После перемещения объект все так же остается жить и должен иметь возможность правильно удалиться как со стека, так и из кучи. Ответственность за это полностью лежит на разработчике класса. Но удаляет объект не семантика перемещения и, тем более, не конструктор перемещения. функция std::move является всего лишь декоратором, который меняет тип объекта с lvalue на rvalue. Она не занимается удалением или очисткой объекта.

    После перемещения объект можно дальше использовать. Если следовать гайдлайну, то после перемещения объект должен стать таким, как будто он был только что сконструирован стандартным конструктором. Объект можно снова заполнить и снова переместить. И так - сколько угодно раз.
    А сам объект всегда занимает один и тот же объем памяти, определенный его классом.

    Так же по теме:
    https://github.com/isocpp/CppCoreGuidelines/blob/m...
    https://github.com/isocpp/CppCoreGuidelines/blob/m...
    Ответ написан
    2 комментария
  • Есть ли какой нибудь удобный класс в библиотеках c++ для работы с датами?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    А еще есть стандартный time_point. Удобный, официальный, с радостью берущий ответственность на себя. :)

    Почитать можно тут:
    en.cppreference.com/w/cpp/chrono/time_point
    Ответ написан
    Комментировать