• Как получить нужную точность при умножении и делении чисел типа double?

    @Mercury13
    Программист на «си с крестами» и не только
    Дело в том, что 4,2 и 4,3 невозможно представить в виде double. И система, например, сохраняет 4,3 в виде 4,2999999, которое при умножении на 10 станет 42,999999.
    Нужно убедиться, что при преобразовании double → int происходит round, а не усечение.
    Ответ написан
    2 комментария
  • Насколько отвратителен данный код?

    @Mercury13
    Программист на «си с крестами» и не только
    Главная проблема.
    const char *source = fmt::format("/tmp/{}.ini", unid).c_str();

    Объект fmt::format сразу же исчезнет, и source будет «висеть».

    Придётся писать…
    fmt::format fmSource("/tmp/{}.ini", unid);
    const char *source = fmSource.c_str();

    И так далее.
    Ответ написан
    8 комментариев
  • Почему не работает код (c)?

    @Mercury13
    Программист на «си с крестами» и не только
    https://ru.wikipedia.org/wiki/Условия_Йоды
    Аж в двух местах.

    Причём в Си не нужно ставить в сложных условиях скобки, как в Паскале, вполне покатит такое…
    if ((a >= 11 && a <= 14) || m == 0 || (m >= 5 && m <= 9))

    Но нет, вы наставили лишних скобок и заглушили ошибку.

    Также, что мне не нравится.
    Вместо 0,1,2,3 лучше использовать константы MANY/FEW/ONE/UNKNOWN.

    В case 1 символ подстановки плохо содран с доски.

    Если условие на MANY самое сложное и выполняется по остаточному принципу — почему его делаем самым первым? Лучше что-то типа…
    • 10…20 → MANY
    • 1 → ONE
    • 2…4 → FEW
    • Иначе MANY
    Ответ написан
    Комментировать
  • В чем причина некоректного отображения текста во всех браузерах?

    @Mercury13
    Программист на «си с крестами» и не только
    Что-то заглючило или в файле шрифта Arial, или в библиотеках Direct2D. И Blink, и «рыжая» используют DirectWrite, если он есть (у вас 10, он точно есть). У Edge свой шрифтовой движок. Разумеется, нужно восстановить соответствующие файлы.
    Больше грешу на шрифт Arial: всё, что вы показываете как ошибку, выведено именно этим шрифтом. Если у вас SSD, заодно проверьте его SMART.
    UPD. Может быть, не сам шрифт виноват, а кэш шрифтов. Прежде надо попробовать это. https://winaero.com/blog/rebuild-font-cache-windows-10/
    Ответ написан
  • Какой формулой определить где находится персонаж - за другим персонажем, перед, справа-слева и тд.?

    @Mercury13
    Программист на «си с крестами» и не только
    Считаем, что у нас FPS, т.е. есть некая абсолютная «горизонталь». Обычно в таких играх взгляд хранится через углы (yaw, pitch, roll).
    Взад-вперёд: знак скалярного произведения двух векторов: единичного вектора взгляда без учёта тангажа (cos(yaw), sin(yaw)) и вектора на персонажа (x1−x0, y1−y0).
    Влево-вправо: знак косого произведения той же пары векторов.
    Вверх-вниз: и так понятно.
    Все три в одних единицах, и у кого модуль больше, то и пишем: сверху, справа и т.д. Возможно, придётся сделать скидку на рост персонажей и их диаметр.

    В играх типа Descent нет верха и низа, горизонтали и вертикали. Верх и низ есть только относительно кабины нашего аппарата, а взгляд хранится в виде ортонормированной матрицы, куска этой матрицы или кватерниона.
    1) Скалярное произведение единичного 3D-вектора взгляда (обычно в таких играх хранится напрямую или считается через кватернион поворота) и 3D-вектора на персонажа.
    2) Скалярное произведение единичного вектора, который смотрит вбок (если такой есть), и вектора на персонажа. Либо смешанное произведение единичного вектора взгляда, единичного вектора макушки и вектора на персонажа.
    3) Скалярное произведение единичного вектора макушки и вектора на персонажа.

    Смешанный случай — с переменным вектором гравитации (в глобальных авиасимуляторах или играх на астероидах) — распиши сам.
    Ответ написан
    Комментировать
  • Как вычислить члены геометрической прогрессии?

    @Mercury13
    Программист на «си с крестами» и не только
    Есть два варианта. 1) Какие попало целые члены; 2) Последовательные целые члены. На вопрос «какие попало члены» (без решения) ответил longclaps, отвечу на второй.
    589952 = a0 (2n−1).
    А теперь разбиваем наше число на множители: 589952 = 27 ∙ 11 ∙ 419.
    Двойки могут идти только в первый член a0. А с 419 и 11 — ни каждое из них, ни их произведение не похожи на 2n−1.
    Так что единственный ответ — единственный член, равный 589952.

    Вы точно переписали цифру 589952?

    Если члены последовательные, но не целые, возможно ∞ ответов — например, 10 членов, первый из которых — 589952/1023.
    Ответ написан
  • Как работает криптор который из exe может сделать doc или подобные форматы?

    @Mercury13
    Программист на «си с крестами» и не только
    Есть две разные задачи.
    1. Просто. Пронести программу через систему безопасности при условии, что твой компьютер — «тихая гавань» и можно спокойно декодировать и запустить. Например, чтобы сбить с толку антивирус Gmail.
    2. Сложно. Твой компьютер — уже не «тихая гавань», на нём действуют системы безопасности, и требуется их как-то обойти. Или, например, мы распространяем вирус, а в роли системы безопасности — бдительный пользователь.

    Первая задача в простейшем случае решается сменой расширения. Можно придумывать и более сложные методы, чтобы антивирус сказал: это действительно DOC.

    Чтобы решить вторую задачу, надо в программе (Word, WinRAR и т.д.) найти эксплойт типа «исполнение произвольного кода».
    Ответ написан
  • Как организовать структуру классов?

    @Mercury13
    Программист на «си с крестами» и не только
    Strategy[N] хранит не наследников Strategy, а объекты класса Strategy. Поэтому в массиве придётся хранить не тела объектов, а указатели.

    Если у вашего МК есть полноценное управление памятью с операцией new, всё просто: std::unique_ptr<Strategy> strategies[N]. Если нет — приходится как-то извращаться, и об этом не будем.

    Как сериализировать? Добавим классу Strategy небольшую функцию
    unsigned char code() const;
    Пусть первая стратегия возвращает, например, 1=CODE_DUPLICATE, а вторая — 2=CODE_MACRO. Сериализация, в числе прочего, будет записывать в память эти коды.

    Считав код, мы создаём Duplicate или Macro, а затем считываем объект этого класса.
    std::unique_ptr<Strategy>& thatObject = strategies[i];
    switch (getSomeByte) {
    case CODE_DUPLICATE:
      thatObject = new DuplicateStrategy();
      break;
    case CODE_MACRO:
      thatObject = new MacroStrategy();
      break;
    default:
      // можно ничего не делать. Можно сообщить об ошибке.
    }
    thatObject->read();
    Ответ написан
    Комментировать
  • Какой вариант применения исключений лучше?

    @Mercury13
    Программист на «си с крестами» и не только
    Декларируемые исключения Java — это плохо. Однако эти декларируемые исключения позволяют осознать, что есть три типа аварий.

    1. Среда исполнения чувствует себя плохо, исполнение программы под угрозой (сработала самопроверка в какой-то части программы, не подконтрольной программисту). Например, срабатывание какого-то встроенного детектора, определяющего запись в чужую память. Выпадают где угодно. Их на практике не приходится ни выбрасывать, ни ловить.

    2. Нарушена логика программы (сработала самопроверка, зависящая от программиста). Неверный аргумент, выход за границу массива. Выпадают где угодно. Их часто выбрасывают и крайне редко ловят.

    3. Нестабильная часть программного окружения чувствует себя плохо. Файл не найден, нет связи. Выпадают в определённых точках. Часто выбрасывают и часто ловят. В Java только их требуется декларировать.

    Таким образом, ваш холивар относится к вопросу: выбрасывать ли исключение, если случилась авария 3-го типа? Я лично замечал подобные вопросы только в одном языке — Си++. Думаю, это связано с двумя вещами. 1) Большие издержки, связанными с определёнными методиками обработки исключений. 2) Довольно невнятное поведение Си++ при выпадении исключения из конструктора.

    Если же отойти от недостатка конкретного языка — даже по поводу обычного HTTP 404 есть «путь Indy» и «путь cURL» — выбросить исключение и сообщить о ненайденном документе другими методами. За первое и за второе можно найти кучу аргументов.
    Ответ написан
    Комментировать
  • Как сделать разную реализацию одной и той же функции класса в C++?

    @Mercury13
    Программист на «си с крестами» и не только
    Перенести пользовательскую функциональность в другое место — так называемый «слушатель».
    using EvClick = void (*)();
    
    Class Model{
    public:
      void click() { if (fOnClick) fOnClick(); }
      void setOnClick(EvClick x) { fOnClick = x; }
    private:
      EvClick fOnClick = nullptr;
    }

    Подобные слушатели есть в любой визуальной оконной библиотеке: VCL, Qt. В VCL так и есть, за исключением вписанных в синтаксис свойств. В Qt для этого используют сигналы-слоты.

    Наладить передачу любых данных в эту функцию — шаблон «команда».
    class ClickEvent {
    public:
      int x, y;
      virtual ~ClickEvent();
    }
    
    using EvClick = void (*)(ClickEvent&);
    Ответ написан
    7 комментариев
  • Как получить адрес наследника?

    @Mercury13
    Программист на «си с крестами» и не только
    Именно так и делать. Потому что от вас требуется наладить шаблон «Public Морозов» в очень тяжёлых условиях (нет даже заголовка). Так что вам придётся повторять то, что обычно делают компилятор и линкер.

    Может помочь устройство таблиц виртуальных методов и dynamic_cast именно в вашем компиляторе. Например, на MinGW у меня получилось вот такое.
    #include <iostream>
    
    
    class A {
    public:
        int a;
        virtual void doA() {}
        virtual ~A() = default;
    };
    
    // class B, C, D, E  аналогично {
    
    class DE : public D, public E { int de; };
    class AB : public A, public B { int ab; };
    class CDE : public C, public DE { int cde; };
    
    class All : public AB, public CDE {};
    
    
    struct Vtable {
        uint32_t d[1];
    };
    
    union PObject {
        void* asVoid;
        Vtable** asVtable;
        char* asRaw;
    };
    
    int main()
    {
        All all;
        All* pAll = &all;
        E* pThis = &all;
        std::cout << pAll << " " << pThis << std::endl;
    
        PObject pX;
        pX.asVoid = pThis;
        while (true) {
            uint32_t offset = -(*pX.asVtable)->d[-2];
            std::cout << "Got offset " << offset << std::endl;
            if (offset == 0)
                break;
            pX.asRaw -= offset;
            std::cout << "Got pointer " << pX.asVoid << std::endl;
        }
    
        return 0;
    }

    До промежуточного потомка (DE или CDE) достучаться не удалось.
    Ответ написан
    3 комментария
  • Как создать отличный сбалансированный уровень?

    @Mercury13
    Программист на «си с крестами» и не только
    https://www.youtube.com/watch?v=0xBJwrm9C8w
    https://www.gdcvault.com/play/1023139/Level-Design...

    https://www.rockpapershotgun.com/2015/01/22/how-to...

    https://www.gamasutra.com/blogs/AsherEinhorn/20150...

    Есть ещё книга «Fundamentals of puzzle and casual game design», но я её пока не видел в лицо.

    И без бета-тестеров никуда. Сам не делал уровни, но один знакомый нарисовал немаленький мод к Braid — бета-тестерами служили его отец и я.

    Вот ещё нашёл по Braid: gamedesignreviews.com/reviews/braid-understanding-...
    Ответ написан
    Комментировать
  • Почему результат выводится inf?

    @Mercury13
    Программист на «си с крестами» и не только
    У нас тут переполнение, 36! не влезает в тип int. Сделайте хотя бы double.

    Inf («машинная бесконечность») — обычно результат дробного деления на ноль. Почему? Посчитаем, сколько двоек в 36!
    Делятся на 2 — 18 сомножителей.
    И ещё на 4 — 9 шт.
    И ещё на 8 — 4 шт.
    И ещё на 16 — 2 шт.
    И ещё на 32 — 1 шт.
    Итого в составе 36! будет 34 двойки, то есть 36! делится на 234. Разумеется, целочисленная арифметика, работающая в кольце остатков от деления на 232, даст ноль. При этом, разумеется, 32-битный регистр многократно и безнадёжно переполнится.

    Более сложный способ расчёта ряда — рекуррентное соотношение. Очередное слагаемое поделим на две части: ai = (2i)! / i²! и bi = |x| / i²!. Постройте, как соотносятся ai−1 и ai, аналогично для b.
    Ответ написан
    Комментировать
  • Почему после определения класса ставится точка с запятой, а после определения функции - нет?

    @Mercury13
    Программист на «си с крестами» и не только
    Это пошло ещё с Си. Так что, простите, ничего плюсового не будет, только «няшная сишка».
    // Переменная безымянного типа
    struct {
      int x, y;
    } point;
    
    // Синоним для типа: вместо struct _Point можно писать просто Point.
    typedef struct _Point {
      int x, y;
    } Point;
    Ответ написан
    Комментировать
  • Приведение типов указателей, как перегрузить?

    @Mercury13
    Программист на «си с крестами» и не только
    Для этого есть специальная операция, dynamic_cast.
    myClass = dynamic_cast<MyClass*>(interface);
    Если interface не MyClass, то вернёт NULL.

    В случае, если через dynamic_cast присваиваем не указатели, а ссылки, выбросит аварию std::bad_cast.

    UPD. Такие преобразования «вниз» — это компромисс между ООП и реальностью, которая вынуждает упрощать интерфейсы. Например, в C++ Builder
    int __fastcall SomeObject::ButtonClick(TObject* Sender)
    {
      TButton* button = dynamic_cast<TButton*>(Sender);
      ...
    }
    Ответ написан
    9 комментариев
  • В чем различие между объявлением с new и без него?

    @Mercury13
    Программист на «си с крестами» и не только
    ПОЛОЖЕНИЕ В ПАМЯТИ
    Без new: static/глобальная — в сегменте данных, локальная — на стеке. В сегменте данных память отводится при компиляции линковке, создать стековый фрейм — две команды процессора.
    С new: в куче. Управление кучей — довольно сложная задача, и если этих new много, программа может начать тормозить.

    ВРЕМЯ ЖИЗНИ
    Без new: объект живёт, пока выполнение находится в данном блоке. При выходе из блока автоматически срабатывает деструктор.
    С new: уничтожаем, когда хотим.

    ИМЕНОВАНИЕ
    Без new: объект привязан к своему имени.
    С new: объект безымянный (имя только у указателя). Потому возможны структуры данных переменного размера: динамические массивы, связанные списки, деревья и прочее.

    РАЗМЕР
    Без new: задан при компиляции. То есть массив на 10 позиций, и точка, больше — только перекомпиляцией.
    С new: произвольный.
    Ответ написан
    Комментировать
  • Как в с++ вывести данные в stdout?

    @Mercury13
    Программист на «си с крестами» и не только
    Вам, как я понял, нужен standard I/O redirection — чтобы то, что мы пишем куда-то, служило входом для процесса FFMPEG, заранее запущенного. В Windows делается это через каналы (pipes) и CreateProcess.
    https://msdn.microsoft.com/en-us/library/windows/d...
    Ответ написан
    Комментировать
  • Что произойдёт после move-касте содержимого shared_ptr?

    @Mercury13
    Программист на «си с крестами» и не только
    После move-каста содержимого shared_ptr мы говорим: объект под shared_ptr идёт на заклание, и в операциях перемещения мы можем потрошить его как угодно — лишь бы объект остался корректным. В данном коде (как я уже писал, он не компилируется) у нас одни ссылки, и не будет ровным счётом ничего: не выполнится ни одна операция перемещения.

    Однако возможна такая ситуёвина, никак не связанная с временными ссылками. Во втором есть неуправляемая ссылка на первый — и при определённых условиях она может стать висячей.

    Если в T2 всё-таки не ссылка, а экземпляр (T1 m_var), возможно, операция перемещения оставляет объект под ptr в некорректном состоянии, и деструктор ~T1() не может его уничтожить, или какая-то из операций над ptr не подозревает, что объект под ptr выпотрошен.

    UPD. Что делает updateSearchResults? Только он может продлить время жизни объекта, сохранив где-то shared_ptr.

    UPD2. Константная временная ссылка (const T&&) никакого смысла не несёт (по факту в STL пару раз используется). «Неизменный» и «можно выпотрошить» — это слегка противоречивые вещи.
    И всё-таки я грешу на updateSearchResults, который сохраняет где-то объекты. Проблема может решиться, если в PoiSearchPlacesLocation ссылку заменить на shared_ptr. И — ВНИМАНИЕ — соответствующим образом подкорректировать сигнатуру конструктора. У shared_ptr есть конструктор, который берёт под управление любой объект-указатель, создавая новый счётчик, но хреново будет, если один и тот же объект управлять двумя разными счётчиками.
    Ответ написан
  • Как отличить "слово" от бессмысленного набора символов?

    @Mercury13
    Программист на «си с крестами» и не только
    Самый простой способ, пришедший на ум.
    Представить слово как цепь Маркова (это именно что теорвер). Вероятности перехода есть в знаменитой книге Жельникова (да и не только там, я уверен).
    Затем высчитываем вероятность пройти в цепи Маркова именно по этому пути. Если вероятность слишком мала — точно рандомный набор символов.

    Без заготовленных ранее словарей никуда — например, слова «Шымкент» или «парашют» с «невозможными» буквосочетаниями (их вероятность в ЦМ будет, разумеется, нулевая).
    Ответ написан
    Комментировать
  • Как решить хитрую задачку про этажи?

    @Mercury13
    Программист на «си с крестами» и не только
    Пусть турнир назначен на этаже X.
    На этажах 1…X живут L’ людей, на этажах X+1…N — H′ людей.
    Вопрос 1. Стоит ли переносить турнир на этаж X+1?
    Тогда для L′ людей добавляется лишний этаж, для H′ — исчезает лишний этаж, и выигрыш будет H′−L′.
    Стоит, если H′ > L′. Не стоит, если H′ < L′. Безразлично, если H′ = L′.

    Вопрос 2. Возможно ли такое: вверх на 1 перенести не удаётся (будет хуже или безразлично), но этаж Y > X оптимальнее (строго)?
    Раз вверх на 1 перенести нельзя, то H’ <= L’.
    Если этаж Y − 1 безразличен с Y, перенесём турнир туда. Если Y − 2 тже безразличен, то туда, и т.д. Остановимся на этаже Z. Видно, что Z > X + 1: если мы в результате этих движений добрались до X+1, то он лучше X, противоречие.
    Раз Z — оптимальный этаж, то у этажа Z−1 > X есть свои L″ и H″, и поскольку с Z−1 на этаж вверх перенести всё же можно, то H″ > L″.
    С другой стороны, при повышении этажа увеличивается L и уменьшается H, и L″ >= L′ >= H‘ >= H″, то есть H″ <= L″. Противоречие.

    Следствие 3. Если турнир выгодно перенести на несколько этажей выше/ниже, то его выгодно перенести и на этаж выше/ниже.

    Задача 4. Пусть на этажах 1…X−1 живут L людей, на этаже X — M людей, на этажах X+1…N — H людей.
    Для простоты считаем, что сверху и снизу есть по фиктивному пустому этажу — то есть теоретически можно (на практически неоптимально) проводить турнир в подвале и на чердаке.
    Задачу с переносом турнира вверх мы уже решали, и условие того, что турнир нельзя (или безразлично) перенести на этаж вверх: H <= L + M.
    Условие того, что турнир можно (или безразлично) перенести с этажа X−1: H + M >= L.
    Поскольку H + M + L = N (кол-во людей), то эти условия можно записать в виде: L + M >= N/2, L <= N/2
    (всё, получили чеканное условие, и ключи от чердака и подвала можно отдать техслужбам гостиницы.)

    Решение: Идём по этажам, считаем людей. Где накопится N/2, там проводим турнир.

    // считаем полное кол-во
    N = 0
    для i = [0..nFloors)
      N += a[i]
    // ищем, где половинка N
    sum = 0;
    для i = [0..nFloors)
      sum += a[i]
      если sum * 2 >= N
        вывести: i
        СТОП

    Расширенное решение: Если накопилось ровно N/2, безразличны данный этаж, все нулевые над ним, и ещё один ненулевой. (Можно без кода?)
    Ответ написан
    Комментировать