-Поиск по дневнику

Поиск сообщений в rss_rss_hh_full

 -Подписка по e-mail

 

 -Постоянные читатели

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 1

Habrahabr








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

PHP-Дайджест № 114 – свежие новости, материалы и инструменты (1 – 14 августа 2017)

Понедельник, 14 Августа 2017 г. 02:21 + в цитатник


Свежая подборка со ссылками на новости и материалы. В выпуске: PHP 7.2.0 Beta 2, об истории и безопасности unserialize(), старт PSR HTTP Client, StackOverflow Driven Development, видео с конференций и митапов, и многое другое.
Приятного чтения!



Новости и релизы


  • PHP 7.2.0 Beta 2 — Список новых возможностей ветки 7.2 тут, а протестировать можно из подготовленного Docker-образа. Третий и последний бета-релиз запланирован на 17 августа.
  • Composer 1.5.0
  • PHP 7.0.22
  • PHP 7.1.8
  • [PSR] HTTP Client — Согласно принятому процессу, новые предложения для PSR должны пройти входное голосование, прежде чем будет разработан черновик. Такое голосование прошло для стандарта HTTP Client, а значит в ближайшем будущем группа начнет работу над документом.


Инструменты


  • mihaeu/dephpend — Инструмент статического анализа, который поможет выявить проблемы в архитектуре.
  • spatie/phpunit-watcher — Инструмент для автоматического запуска тестов когда изменен код. Пост в поддержку.
  • Imangazaliev/DiDOM — Простая и быстрая библиотека для парсинга HTML.
  • hirak/prestissimo — Плагин Composer для параллельной установки пакетов. Значительно ускоряет установку зависимостей. Прислал chilic.
  • clue/php-commander — Инструмент для создания простых консольных удобным с интерфейсом.
  • thephpleague/json-reference — Библиотека для работы с JSON-ссылками.
  • paragonie/easydb — Простая и лаконичная обертка над PDO.
  • hjr3/php-rpm — Менеджер процессов для PHP на Rust.


Материалы для обучения




Аудио и видеоматериалы




Занимательное



Спасибо за внимание!

Если вы заметили ошибку или неточность — сообщите, пожалуйста, в личку.
Вопросы и предложения пишите на почту или в твиттер.

Прислать ссылку
Быстрый поиск по всем дайджестам
<- Предыдущий выпуск: PHP-Дайджест № 113

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335546/


Метки:  

[Из песочницы] Расчет приоритета комбинаций в техасском холдеме (покере) на PHP

Воскресенье, 13 Августа 2017 г. 20:52 + в цитатник
В этой статье я расскажу про мой способ расчета приоритета комбинаций в техасском холдеме (покере с 2 карманными картами и 5 общими). Условно разделю статью на 6 частей: Создание массива карт, Расчет Стрита, Расчет Флеша и Стрит Флеша, Расчет Парных комбинаций, Окончательный расчет, Вывод на экран. Код пишу в процедурном стиле.

Цель: получить на выходе программы удобочитаемый результат, доступный для дебаггинга и отладки еще на стадии написания кода. Для достижения поставленной цели делаю разницу между комбинациями в 1e+2 (100) раз.

Примерный вид того, что должно получиться в итоге, где 2 последние карты считаю карманными, а первые 5 общими:


Создание массива карт


В колоде 4 масти и 13 достоинств, поэтому массив из 7 чисел буду формировать в диапазоне [2 — 14, 102 — 114, 202 — 214, 302 — 314], а далее оперировать остатками от деления на 100 (%100) этих чисел. Можно, конечно, формировать массив из чисел в диапазоне [2 — 14, 22 — 34, 42 — 54, 62 — 74], а затем сравнивать остатками от деления на 20 (%20). Но удобочитаемость первого варианта по-моему на лицо, поэтому остановлюсь на нем.

function cardsCreation()
{
    $arrayCards = [];
    for ($i = 0; $i < 7; $i++) {
        $card = mt_rand(2, 14); // создаю случайное число в диапозоне от 2 до 14
        $multiplier = mt_rand(0, 3); //формирую случайным образом множитель
        if (1 == $multiplier) { //если множитель равен 1, добавляю к числу 100
            $card = $card + 100;
        } else if (2 == $multiplier) { //если множитель равен 2, добавляю к числу 200
            $card = $card + 200;
        } else if (3 == $multiplier) { //если множитель равен 3, добавляю к числу 300
            $card = $card + 300;
        }

        if (!in_array($card, $arrayCards)) { //проверяю, есть ли в массиве такое число, если нет добавляю его в массив
            $arrayCards = array_merge($arrayCards, [$card]);
        } else { // если число есть в массиве, откатываю цикл, чтобы сформировать новое число
            $i--;
        }
    }
    return $arrayCards;
}

Определение и расчет комбинации Стрит


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

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

Суть моего метода определения стрита сводится к тому, что после сортировки я буду проверять сначала первые пять карт на стрит, затем вторые и третьи. Также не забываю что туз, двойка, тройка, четверка и пятерка тоже составляю стрит. Поэтому, если в наборе есть туз (значение 14), к массиву добавляю восьмой элемент — 1. И следовательно в этом случае нужно проверить четвертую пятерку карт.

function straight(array $arrayCards) {
    $newArrayCards = [];
    $ace = false;
    foreach ($arrayCards as $arrayCard) {
        $newArrayCards = array_merge($newArrayCards, [$arrayCard % 100]);  //создаю массив с остатками от деления на 100
        if(14 == $arrayCard % 100) { //проверка на туз для комбинации А, 2, 3, 4, 5
            $ace = true;
        }

    }
    if($ace == true) {
        $newArrayCards = array_merge($newArrayCards, [1]); //если в массиве присутствует туз, добавляю к массиву 1
    }
    rsort($newArrayCards); //сортирую массив с числами в порядке убывания

    $count = 0; //счетчик, к которому добавляется 1 в случае, если разница между текущим элементом цикла и предыдущим = 1
    $length = 4; //число, показывает до какого элемента массива проверять
    $result = 0; //окончательный результат
    $begin = 0; //число показывает, с какого элемента массива проверять
    for($i = 1; $i <= $length; $i++) {
        if (-1 == ($newArrayCards[$i] - $newArrayCards[$i - 1])) {
            $count++;
        }
        if($length == $i) {
            if(4 == $count) { //$count == 4, когда стрит
                $result = $newArrayCards[$begin] * 1e+8;
            }
            else if($length < 6 or (6 == $length and $ace == true)) {//если стрита нет, идем еще на один круг
                $length++; //увеличиваем число, до которого будем проверять
                $begin++; //увеличиваем число, с которого будем проверять
                $i = $begin; //при попадании в for к $i автоматически добавится 1 ($i++ в for)
                $count = 0; //обнуляю счетчик
            }
        }
    }
    return $result;
}

Определение и расчет комбинаций Флеш и Стрит Флеш


Флеш — комбинация из карт одной масти. При расчете нужно учитывать все карты, составляющие комбинацию.

При расчете стрит флеша, как и при расчете стрита, можно учитывать только старшую карту комбинации.

Чтобы определить флеш, пробегусь циклом по массиву. В цикле создам 4 вспомогательных массива (по количеству мастей). В перый массив положу все карты первой масти, во второй — второй и т.д. Если какой-то масти нет, соответствующий массив будет пустой. Если количество элементов массива больше либо равно 5, значит данная масть образовала флеш.

Флеш более приоритетен, чем стрит, поэтому именно в функции флеша логично проверить не образовала ли комбинация стрит флеш. Также, думаю, понятно, что неудобно писать отдельную функцию, которая проверяет и стрит, и флеш. Лучше написать функцию, которая проверяет массив на флеш, и в ней же вызвать функцию, которая проверяет флеш на стрит.

Если флеш не образует стрит флеш, отсортирую остатки от деления на 100 (%100) исходного массива. Далее умножу первые пять членов полученного массива на 1е+10, 1е+8, 1е+6, 1е+4, 1е+2 соответственно.

Функцию по определению стрит флеша построю на основе функции по определению стрита, с той лишь разницей, что нужно учесть, что в функцию может прийти массив с количеством элементов меньше 7 (я ведь туда отправляю только те карты, которые образуют флеш).

Функция для определения Флеша:

function pokerFlush(array $arrayCards) {
    $suit1 = []; //первая масть
    $suit2 = []; //вторая масть
    $suit3 = []; //третья масть
    $suit4 = []; //четвертая масть
    foreach ($arrayCards as $arrayCard) { //создаю 4 массива, содержащих разные масти исходного массива
        if($arrayCard >= 2 and $arrayCard <= 14) {
            $suit1 = array_merge($suit1, [$arrayCard]);
        } else if($arrayCard >= 102 and $arrayCard <= 114) {
            $suit2 = array_merge($suit2, [$arrayCard]);
        } else if($arrayCard >= 202 and $arrayCard <= 214) {
            $suit3 = array_merge($suit3, [$arrayCard]);
        } else {
            $suit4 = array_merge($suit4, [$arrayCard]);
        }}
    if(count($suit1) >= 5) {  //если количество карт первой масти больше или равно 5
        $result = straightFlush($suit1); //проверяю не образует ли данная комбинация стрит флеш
        if(0 == $result) {//если стрит флеша нет
            foreach ($suit1 as $key1 => $s1) {//выбираю остатки от деления на 100
                $suit1[$key1] = $s1 % 100;
            }
            rsort($suit1); //сортирую массив по убыванию
            $result = $suit1[0] * 1e+10 + $suit1[1] * 1e+8 + $suit1[2] * 1e+6 + $suit1[3] * 1e+4 + $suit1[4] * 1e+2;
        }
    } else if (count($suit2) >= 5) {   //если количество карт второй масти больше или равно 5
        $result = straightFlush($suit2);
        if(0 == $result) {
            foreach ($suit2 as $key2 => $s2) {
                $suit2[$key2] = $s2 % 100;
            }
            rsort($suit2);
            $result = $suit2[0] * 1e+10 + $suit2[1] * 1e+8 + $suit2[2] * 1e+6 + $suit2[3] * 1e+4 + $suit2[4] * 1e+2;
        }
    } else if (count($suit3) >= 5) { //если количество карт третьей масти больше или равно 5
        $result = straightFlush($suit3);
        if(0 == $result) {
            foreach ($suit3 as $key3 => $s3) {
                $suit3[$key3] = $s3 % 100;
            }
            rsort($suit3);
            $result = $suit3[0] * 1e+10 + $suit3[1] * 1e+8 + $suit3[2] * 1e+6 + $suit3[3] * 1e+4 + $suit3[4] * 1e+2;
        }
    } else if (count($suit4) >= 5) {     //если количество карт четвертой масти больше или равно 5
        $result = straightFlush($suit4);
        if(0 == $result) {
            foreach ($suit4 as $key4 => $s4) {
                $suit4[$key4] = $s4 % 100;
            }
            rsort($suit4);
            $result = $suit4[0] * 1e+10 + $suit4[1] * 1e+8 + $suit4[2] * 1e+6 + $suit4[3] * 1e+4 + $suit4[4] * 1e+2;
        }
    } else {
        $result = 0;
    }
    return $result;
}

Функция для определения Стрит Флеша (аналогична Стриту, только нужно учесть, что количество элементов массива может быть не только 7, но еще и 5, и 6, и что возвращаемый результат этой функции должен быть больше):

function straightFlush(array $arrayCards) {
    $newArrayCards = [];
    $ace = false;
    foreach ($arrayCards as $arrayCard) {
        $newArrayCards = array_merge($newArrayCards, [$arrayCard % 100]);  
        if (14 == $arrayCard % 100) {
            $ace = true;
        }
    }
    if ($ace == true) {
        $newArrayCards = array_merge($newArrayCards, [1]);
    }
    rsort($newArrayCards); 
    $count = 0; 
    $length = 4; 
    $result = 0; 
    $begin = 0; 
    for ($i = 1; $i <= $length; $i++) {
        if (-1 == ($newArrayCards[$i] - $newArrayCards[$i - 1])) {
            $count++;
        }
        if ($length == $i) {
            if (4 == $count) { 
                $result = $newArrayCards[$begin] * 1e+16;
            } else if ((7 == count($arrayCards) and ($length < 6 or (6 == $length and $ace == true))) or
                (6 == count($arrayCards) and ($length < 5 or (5 == $length and $ace == true))) or //если число элементов исходного массива = 6
                (5 == count($arrayCards) and (5 == $length and $ace == true))) { //если число элементов исходного массива = 5
                $length++; 
                $begin++; 
                $i = $begin;
                $count = 0;
            }
        }
    }
    return $result;
}

Определение и расчет парных комбинаций


Существует пять парных комбинаций: каре (4 карты одного достоинства), фулл хаус (3 карты одного достоинства и 2 карты другого достоинства), тройка (3 карты одного достоинства), две пары (2 карты одного достоинства и 2 карты другого достоинства), пара (2 карты одного достоинства).

Важно при расчете помнить, что в комбинации фулл хаус главным является сочетание из трех карт, а из двух второстепенным, т.е., например, комбинацию из 3-х двоек и 2-х десяток будем считать, как 3*1e+12 + 10*1e+10, а не по достоинству карт.

А также при расчете парных комбинаций нужно учитывать проблемы:

1. То, что более 5 карт образуют комбинацию.
2. То, что у нас может возникнуть ситуация, при которой комбинаций более одной.

Для подсчета совпадения карт буду использовать три счетчика, которые будут считать парные карты в колоде. Там, где счетчик равен 1 — участвуют 2 карты в комбинации, где счетчик 2 — 3 карты, где 3 — 4 карты.

Рассмотрим все возможные варианты сочетания счетчиков:

100 — пара
110 — 2 пары
111 — 3 пары (нужно преобразовать к виду 110 (две пары))
200 — тройка
210 — фулл хаус
120 — фулл хаус (нужно преобразовать к виду 210 (фулл хаус))
211 (121, 112) — фулл хаус + пара (нужно преобразовать к виду 210 (фулл хаус))
220 — 2 тройки или фулл хаус + 1 карта (нужно преобразовать к виду 210 (фулл хаус))
300 — каре
310 (130) — каре + пара (нужно преобразовать к виду 300 (каре))
320 (230) — каре + тройка (нужно преобразовать к виду 300 (каре))
Далее произведу окончательный расчет парных комбинаций.

function couple(array  $arrayCards) {
    $newArrayCards = [];
    foreach ($arrayCards as $arrayCard) {
        $newArrayCards = array_merge($newArrayCards, [$arrayCard % 100]); //создаю массив с остатками от деления на 100
    }
    rsort($newArrayCards); //сортирую массив с числами в порядке убывания

    $count1 = 0; //счетчик для первой пары
    $count2 = 0; //счетчик для второй пары
    $count3 = 0; //счетчик для третьей пары

    $match1 = 0; //сюда положу значение первой пары
    $match2 = 0; //сюда положу значение второй пары
    $match3 = 0; //сюда положу значение третьей пары


    for($i = 1; $i < count($newArrayCards); $i++) {

        if ($newArrayCards[$i] == $match1 or $match1 == 0) { //первое парное сочетание
            if ($newArrayCards[$i] == $newArrayCards[$i - 1]) {
                $match1 = $newArrayCards[$i];
                $count1++;
            }
        } else if ($newArrayCards[$i] == $match2 or $match2 == 0) { //второе парное сочетание
            if ($newArrayCards[$i] == $newArrayCards[$i - 1]) {
                $match2 = $newArrayCards[$i];
                $count2++;
            }
        } else if ($newArrayCards[$i] == $match3 or $match3 == 0) { //третье парное сочетание
            if ($newArrayCards[$i] == $newArrayCards[$i - 1]) {
                $match3 = $newArrayCards[$i];
                $count3++;
            }
        }
    }

    //здесь я преобразую 111 к 110 (2 пары) и 211 к 210 (фулл хаус)
    if(($count1 == 1 or $count1 == 2) and $count2 == 1 and $count3 == 1) {
        $count3 = 0;
    }
    //здесь я преобразую 121 сначала к 211 для простоты вычислений, а затем к 210 (фулл хаус)
    else if($count2 == 2 and $count1 == 1 and $count3 == 1) {
        $support = $match2;
        $match2 = $match1;
        $match1 = $support;
        $count1 = 2;
        $count2 = 1;
        $count3 = 0;
    }
    //здесь я преобразую 112 сначала к 211 для простоты вычислений, а затем к 210 (фулл хаус)
    else if($count3 == 2 and $count1 == 1 and $count2 == 1) {
        $support = $match3;
        $match2 = $match1;
        $match1 = $support;
        $count1 = 2;
        $count3 = 0;
    }
    //здесь я преобразую 220 к 210 (фулл хаус)
    else if($count1 == 2 and $count2 == 2 and $count3 == 0) {
        $count2 = 1;
    }
    //здесь я преобразую 120 к 210 (фулл хаус)
    else if ($count1 == 1 and $count2 == 2 and $count3 == 0) {
        $support = $match1;
        $match1 = $match2;
        $match2 = $support;
        $count1 = 2;
        $count2 = 1;
    }
    //320 к 300 и 310 к 300
    else if($count1 == 3 and ($count2 == 2 or $count2 == 1)) {
        $count2 = 0;
    }
    //230 к 320 и затем к 300 и 130 к 310 и затем к 300
    else if($count2 == 3 and($count1 == 2 or $count1 == 1)) {
        $support = $match2;
        $match2 = $match1;
        $match1 = $support;
        $count1 = 3;
        $count2 = 0;
    }

    //каре
    if ($count1 == 3) {
        $count1 = 1e+14;
    }
    //фулл хаус
    else if ($count1 == 2 and $count2 == 1) {
        $count1 = 1e+12;
        $count2 = 1e+10;
    }
    //тройка
    else if ($count1 == 2 and $count2 == 0) {
        $count1 = 1e+6;
    }
    //2 пары
    else if ($count1 == 1 and $count2 == 1) {
        $count1 = 1e+4;
        $count2 = 1e+2;
    }
    //пара
    else if ($count1 == 1 and $count2 == 0) {
        $count1 = 1e+2;
    }

    $result = $match1 * $count1 + $match2 * $count2;// $match1 и $match2 будут равны 0, если совпадений не было
    return $result;
}

Окончательный расчет


Теперь у нас имеются функции, которые проверяют массив карт на стрит, флеш, стрит флеш и парные комбинации. Вызову их вместе и посчитаю окончательный результат, не забываю также и про расчет старшей карты.

Расчет старшей карты:

1. Определю наибольшую и наименьшие по приоритетности карманные карты. Для этого сравню их остатками от деления на 100 (%100).
2. К наибольшей по приоритетности карте добавлю наименьшую по приоритетности деленную на 100.

function priority(array  $arrayCards) {
    //здесь я определяю старшую карту
    if($arrayCards[5] % 100 > $arrayCards[6] % 100) { //условились, что две последние карты массива - карманные
        $highCard1 = $arrayCards[5] % 100;
        $highCard2 = $arrayCards[6] % 100;
    } else {
        $highCard1 = $arrayCards[6] % 100;
        $highCard2 = $arrayCards[5] % 100;
    }


    $flush = pokerFlush($arrayCards); //вызываю функцию для расчета флеша
    $straight = straight($arrayCards); //вызываю функцию для расчета стрита
    $couple = couple($arrayCards); //вызываю функцию для расчета пар

    //далее определяю результат согласно приоритету комбинаций
    if($flush >= 1e+16) {
        $result = $flush; //стрит флеш
    } else if($couple >= 1e+14 and $couple < 1e+16) {
        $result = $couple; //каре
    } else if($couple >= 1e+12 and $couple < 1e+14) {
        $result = $couple; //фулл хаус
    } else if($flush >= 1e+10) {
        $result = $flush; //флеш
    } else if($straight >= 1e+8 and $straight < 1e+10) {
        $result = $straight; //стрит
    } else if($couple >= 1e+6 and $couple < 1e+8) {
        $result = $couple; //тройка
    } else if($couple >= 1e+4 and $couple < 1e+6) {
        $result = $couple; //две пары
    } else if($couple >= 1e+2 and $couple < 1e+4) {
        $result = $couple; //пара
    } else {
        $result = $highCard1 + $highCard2 * 1e-2; //старшая карта
    }
    return $result;
}

О выводе на экран


Выводила я результат в браузер, используя вот такой спрайт (брала картинку с сайта: fondhristianin.ru/?p=2941), как background-image для div.



Ширину и высоту для div задавала равными размеру одной карты, т.е. если размер изображения 572px*328px (как в данном случае), а в ширину количество карт равно 13, в высоту — 5, ширину и высоту задавала 44px*65.6px. Далее меняла background-position с учетом ранее сформированного массива карта.

Расчет background-position для любого из divов по оси х:

100/12 * ($arrayCards[$i] % 100 - 2)

где $arrayCards — ранее сформированный массив.
$i — порядковый номер карты.

Пояснения к расчету:
В ряду 13 карт, начало 1-ой карты — 0%, начало 13-ой карты — 100%, поэтому разница позиций — 100% / 12 (а не 13). Приводим числа карт из вида [2-14, 102-114, 202-214, 302-314] к [0-12]. Для этого возьмет остаток от числа карты и отнимем от остатка 2. Умножим полученное число на разницу позиций.

Расчет background-position для любого из divов по оси y:

100/4 * floor($arrayCards[$i] / 100)

Пояснение к расчету:
В ряду 5 карт, начало 1-ой карты — 0%, начало 5-ой карты — 100%, поэтому разница позиций — 100% / 4 (а не 5). Приводим числа карт из вида [2-14, 102-114, 202-214, 302-314] к [0-3]. Для этого разделим число карты на 100 и округлим в меньшую сторону (можно использовать и простую операцию округления, она сработает аналогичным образом). Умножим полученное число на разницу позиций.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335540/


Метки:  

TSP problem. Mixed algorithm

Воскресенье, 13 Августа 2017 г. 18:42 + в цитатник

Метки:  

Итак, вы решили развернуть OpenStack

Воскресенье, 13 Августа 2017 г. 18:30 + в цитатник

Вы наверняка слышали об OpenStack. Блин, да о нем говорят на каждом более-менее связанном мероприятии. Все кому не лень пропагандируют OpenStack. Модно, молодежно, все уже есть, Open Source, вливайся давай. И вот наслушавшись тонны маркетингового булшита, вы решаетесь: Будем ставить OpenStack!

Я не проводил специальных изысканий на этот счет, но отрицательных отзывов о нем вроде бы не так много, по крайней мере на русском. На первый взгляд все выглядит просто фантастически. Что ж, извольте представить мой личный пост ненависти к OpenStack.

Предыстория


Об OpenStack я узнал на одной из конференций по OSS году примерно в 2012-м. В то время я работал в компании активно использующей большие кластера (100+ машин), однако без виртуализации, оркестрирования (kickstarter + IBM CSM в счет?) и в основном с пакетным исполнением задач: запустил, отработало, забрал результат. Полноценного понимания что такое облака и зачем они нужны еще не было, но интерес возник. Конечно сразу же возникло желание развернуть новую крутую штуку, которая вот прям сейчас сделает все хорошо.

Чуть позже в том же году, такая возможность представилась, правда уже в рамках стартапа. Нужно было развернуть проект, использующий кластеризацию и горизонтальное масштабирование, при этом нужна была изоляция окружений и совместное использование ресурсов одной машины несколькими службами — бюджет все-таки не резиновый.

В общем, в 2012-м мы развернули OpenStack, на тот момент это был Essex, запустили проект, прожили с такой облачной инфраструктурой до 2014-го года, кажется до релиза Grizzly включительно. Сил и желания поддерживать его дальше не было, и OpenStack был с позором выпилен.

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

Итак, приступим, что же мне не нравится в Openstack и весьма вероятно не понравится вам.

Он чересчур комплексный


Даже не так. Он, *****, МОНСТРУОЗНЫЙ. Нет, взгляните сами.



Когда-то давно, когда мы ставили Essex, там было все относительно просто и понятно. Keystone (служба авторизации), Glance (служба хранилища образов) и Nova (служба управления гипервизорами). Кроме того там еще был Horizon (дашборд) и куча мелких и не очень зависимостей. Каждый узел системы обрастает чуть ли не десятками вспомогательных демонов. На controller node через некоторое время становится страшно смотреть.



Так, когда наш виртуальный кластер приблизился к 20 серверам, controller node стал безбожно тормозить, по непонятной причине. Ну точнее понятной, но мне непонятно, зачем identity service грузила процессор на 100%?

Из комплексности происходит следующий недостаток.

Он запутанный


Архитектура OpenStack достаточно сильно фрагментирована. Есть очень большое количество «движущихся частей», взаимосвязь который между собой не всегда абсолютно ясна. У вас что-то сломалось? Окей, попробуй понять где это что-то сломалось и почему. OpenStack Foundation похоже гордится, что в OpenStack более 20 миллионов строк кода, даже на главную своего сайта вынесли. Так вот, ЭТО НИФИГА НЕ ДОСТОИНСТВО.

Код в большинстве своем написан на Python. Спасибо, OpenStack, благодаря тебе я возненавидел Python и все что с ним связано. Возможно сейчас с документацией получше, но раньше ее практически не было. Логика вполне может начинаться в одном демоне, а потом с помощью запросов через RabbitMQ исполнятся совершенно в другом и даже на другой машине. Стоит ли говорить, что писать собственные расширения для OpenStack совсем не просто. Одно дело если это просто небольшой хак, другое дело если это полноценный подключаемый модуль с новым функционалом. 5 строчками кода вы точно не обойдетесь.

Если вам надо залезть под капот, чтобы понять что там происходит…
image

Дело в том, что являясь OSS, OpenStack пытается быть kind of unix-way. Т.е. под капотом все эти монструозные службы на самом деле дергают десятки и сотни unix-утилит по собственной логике, которую вам придется изучить и возможно даже дебажить. С документацией, по крайней мере раньше было, все плохо. Зачем вам рассказывать какие именно правила iptables мы добавляем на хост и для чего? У нас же суперкрутое приложение которое делает все само и не требует вашего вмешательства. Хотите добавить свои правила? Нуу, удачи, исходники-то открыты.

Пока вы более-менее вписываетесь в сценарий, предполагаемый авторами — все более-менее окей. Если же вам понадобилось сделать шаг в сторону, ждите трудностей. Запасайтесь man'ами, терпением, возможно вам придется изучить еще несколько несвязанных напрямую с задачей проблем, например как работает RabbitMQ и что ему еще надо?

Из этого проистекает следующая проблема.

Он ненадежный


Казалось бы логично, чем сложнее система, тем менее она надежна. Но видимо эта истина не для всех. Готовьтесь искать источник подземных стуков, запускать демоны в ПРАВИЛЬНОЙ последовательности, много гуглить и читать логи, копаться в исходниках и вот это все.

Некоторые решения на мой взгляд просто спорные. Служба метаданных на виртуальном IP-адресе эмулируемом через iptables? Серьезно? Очень «надежная» работа dnsmasq по выдаче IP виртуалкам. Тысячи их.

И это усугубляется тем что.

Он становится еще больше


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

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

  • Identity service (keystone)
  • Image service (glance)
  • Compute service (nova)
  • Networking service (neutron)
  • Dashboard (horizon)
  • Block Storage service (cinder)
  • Bare Metal service (ironic)
  • Container Infrastructure Management service (magnum)
  • Database service (trove)
  • DNS service (designate)
  • Key Manager service (barbican)
  • Messaging service (zaqar)
  • Object Storage services (swift)
  • Orchestration service (heat)
  • Shared File Systems service (manila)
  • Telemetry Alarming services (aodh)
  • Telemetry Data Collection service (ceilometer)


Да, формально не обязательно ставить абсолютно все службы. Но не думайте что какой-то пустячок, которого нет в основной службе можно будет легко и просто добавить в вашу инфраструктуру.

Например, во времена Essex не было простого способа добавить запись о вашей новой виртуалке в ваш DNS-сервис. Руками — пожалуйста. Хуки? Не, не слышали. Я рождения designate так и не дождался.

А еще знаете что?

Он ломается


От релиза к релизу. Т.е. ну вот вы наконец-то запилили инфраструктуру своей мечты, все худо-бедно работает как рассчитывали, но не хватает одной мааааленькой детали. А в новом релизе она есть. Ну по крайней мере по Release Notes.

Окей гугл, давай обновим наш OpenStack. И тут выясняется, что функционал, который вы с радостью использовали — выпилили. Ну потому что. Ну некрасивый он был и вообще, мы лучше сделаем, честно-честно. В следующем релизе. Может быть. А пока вот вам попроще, но ведь работает же! Ну и плевать что не в вашем случае, но работает!

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

А в следующем релизе ребята из OpenStack решили что баста, мы выпилим функционал работы с сетью в отдельный модуль (будущий neutron), реализуем что-нибудь простое вроде один роутер на виртуальную сеть ну и играйтесь пока. Т.е. нужно было бы пустить весь трафик целого кластера через единичный узел нашей сети, он бы и стал бутылочным горлышком.

В итоге, OpenStack это такая штука, которая вот действительно, работает — не трогай. Даже не пытайся туда свои рученки совать, п**р. Лучше даже на всякий случай хосты не перезагружать, а то вдруг оно больше не поднимется? Любое обновление — это аврал, нервы, ненависть.

Он сырой (местами)


А еще очень дивное чувство испытываешь, когда тебе нужен функционал, ну, скажем, деление на зоны. Ну вот есть у тебя машины с большими винтами, есть с SSD, есть с видюхами, хочу разбить кластер на зоны, чтобы виртуалка падала на ту машину, у которой необходимый ресурс есть. Ну ок, читаем доку, вроде бы availability zones подходит. Настраиваем, включаем. И ничего. В доке написано что все должно, а на практике ничего. Лезем в код, а там.



Будет реализовано. В следующем релизе. Может быть. Ну ты понял. Смотри предыдущий пункт.



Есть ли плюсы, какие будут выводы?


Лично для меня вывод простой. Ванильную версию OpenStack лучше не использовать. Я не знаю как обстоят дела с вендорскими версиями и у всяких контор, торгующих инсталляциями «под ключ», но как по мне это несколько противоречит самой идеологии «свободного облака». Мы опять получаем vendor lock-in, только под другим соусом.

Хотите попытать счастье с ванильным OpenStack? Нет, ну в целом пожалуйста. Плагинов много, комьюнити большое, маркетингового булшита вообще выше крыши. Удачи, в общем. Но для небольших и средних инсталляций я бы скорее не советовал.

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

В общем после полутора лет борьбы с OpenStack мы от него отказались и перешли на другое облако. Управление инфраструктурой стало простым и приятным, а обновлять версий также просто как apt dist-upgrade.

Что это за облако и почему оно удобнее OpenStack я постараюсь рассказать в следующей статье. (СПОЙЛЕР: это OpenNebula).
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335530/


Метки:  

5 популярных мифов о дизайне логотипов

Воскресенье, 13 Августа 2017 г. 18:26 + в цитатник
image

За четыре года мы сделали тысячи логотипов для клиентов со всего мира. И очень часто мы слышим одни и те же заблуждения о дизайне логотипов.
В этой статье мы обсудим пять самых распространенных мифов и постараемся расставить точки над «i».

Миф первый: Логотип должен показывать сферу деятельности



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

Посмотрите вокруг: как часто вы видите на логотипе автопроизводителя машину?
А куртку на логотипе изготовителя курток? Тем не менее, Mitsubishi — это машины, Starbucks — кофе, а Apple —  смартфоны.

Чаще всего ваш логотип увидят по запросу «поесть», «купить машину» или «подстричься». Лого — это способ быть узнаваемым, а не ответ на вопрос «чем вы занимаетесь?».

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

Если же цель: развить бизнес до уровня, когда любой товарный знак будет ассоциироваться с вашим продуктом — будьте оригинальными и не бойтесь экспериментов.

Миф второй: Ценность логотипа в затраченных усилиях


Представьте, что вы заказали логотип для компании которая шьет спортивную одежду. Двадцать тысяч рублей перечислены на карту фрилансера и вот спустя две недели он присылает вам картинку. На ней вот это:

image

Вы, конечно, спросите у фрилансера сколько секунд у него ушло на это… эту «сопельку»? А затем заставите переделывать логотип еще десяток раз,
чтобы проклятый «дизайнер» хоть как-то отработал потраченные деньги.

Такие ситуации происходят постоянно и раздраженных заказчиков можно понять.
Диссонанс между реальными деньгами и логотипом который физически рисуется
за пару секунд — слишком велик. Начинаются бесконечные правки и переделки, причем в результате лучшим все равно оказывается первый вариант.
Проблема в том, что многие люди не до конца понимают дизайн и измеряют его ценность в затраченных усилиях.

Иногда хорошие вещи хороши, не потому что на их создание ушли годы.
Если логотип эффективен, то не важно насколько он сложный. «Сопелька» Nike
из примера выше —  это образец хорошего дизайна. А ошибки которые дизайнер не допустил, бывают куда важнее чем виртуозная работа с графикой. Хороший дизайн — незаметен.

Логотип без фирменного стиля — бесполезен. Если вы знаете где будет использоваться лого, видите идеальный образ компании и ее аудиторию, тогда стоит обратиться к профессиональному дизайнеру и заказать у него не только логотип
но и весь фирменный стиль компании. А если вы не поняли о чем речь —
к профессиональному дизайнеру стоит обратиться тем более. Когда есть стиль,
то логотип перестает быть “галочкой”, вокруг него организована целая концепция и поэтому он приносит объективную пользу.

Миф третий: В логотипе должен быть скрытый смысл



image

Разгадывать ребусы здорово — мозг любит работать и мы испытываем удовлетворение от решенной задачи. Логотипы с загадками и скрытым смыслом встречаются часто. Дать отсылку на легенду компании, ее корпоративные ценности — это высший пилотаж для создателя логотипа.

С другой стороны, логотипов в которых нет никакого скрытого смысла намного больше. Причина — отсутствие практической пользы.
В среднем, у человека есть около секунды чтобы заметить логотип, при этом детали он почти не запоминает. В апреле Логомашина провела небольшой эксперимент , в рамках которого прохожие по памяти рисовали логотипы известных компаний — Lacoste, Apple, Yota, Peugeot, Qiwi, Мегафон и других.
Опыт показал что люди помнят только общие черты известных лого, иногда затрудняясь даже с формой и цветом.

Не смотря на это, снова увидев логотип на вывеске человек сразу же узнает его и идентифицирует с компанией. В этом смысл дизайна. Влияет ли на этот процесс скрытый смысл заложенный в логотипе? Не думаю. Стрелочка FedEx, улыбка Amazon или рукопожатие клиента и производителя на логотипе Hyundai стали известны только после того, как стала известна сама компания. Никто не сможет точно сказать
помогла ли им вложенная в логотип идея.

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

Миф четвертый: Похожие логотипы — это плохо



image

Конечно, если ваш логотип — копия с лого конкурента, это плохо. Клиенты будут путаться.

Но ударяться в оригинальность ради оригинальности — ничуть не лучше. У вас может быть настолько оригинальный логотип, что вас просто не поймут. Клиент обратится к вашим, более спокойным, конкурентам.

К тому же, сфера деятельности всегда диктует определенные стандарты, которые работают как опознавание «свой-чужой». Взгляните на лого кофейных или автомобильных компаний. Там, признанное абсолютным злом наличие общих элементов и похожие логотипы — скорее правило.

Поиск «плагиата” действительно интересен, но на эффективности логотипов Puma и Jaguar, CNN и SEGA, Promt и Google Drive, Land Rover и Ford, Gucci и Chanel — это никак не сказывается. Вы никогда не спутаете эти компании. То, что цель дизайнера делать каждый логотип абсолютно непохожим на другие — это стереотип. Буква «A» похожа на букву «A». и с этим ничего не поделаешь. Тоже самое касается всех графических решений и большинство логотипов отличаются друг от друга только деталями.

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

Миф пятый: Логотип — это очень важно



Конечно, логотип важен, но его значимость часто переоценивают. Логотип не загипнотизирует клиентов, не важно, насколько крутое агентство его сделало и сколько ваших коллег сказали: «Классный лого!».

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

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

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

Одна картинка с лого, какой бы прекрасной она не была, не принесет результата.
Знак будет успешным, если правильно его использовать. Он должен размещаться на вывесках, форме сотрудников, продукции и других носителях оформленных в соответствии с фирменным стилем компании. В случае успеха —  логотип прекрасный способ поселиться в умах и сделать свой символ “именем нарицательным”. Миллионы покупают китайские футболки в интернет-магазинах, просто потому на них изображен зеленый крокодил Lacoste или “вжух” от Nike.

Вывод



Все мифы о дизайне основаны не на пустом месте. Тем не менее строгие правила скорее подразумеваются, чем существуют на самом деле. Мы разобрали самые распространенные стереотипы связанные с ролью логотипа, что на нем должно быть изображено, поговорили о плагиате и значении фирменного стиля. Не сражайтесь с ветряными мельницами, пусть покажут вам направление ветра.

Мы также сделали видеоверсию этой статьи, посмотреть можно здесь:





Текст: Николай Романов
Видео: Данила Баранов, Роман Горбачёв
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335536/


Метки:  

Дайджест интересных материалов для мобильного разработчика #216 (6 августа — 13 августа)

Воскресенье, 13 Августа 2017 г. 16:28 + в цитатник
В центре этой недели – история противостояния разработчиков, ДИТа Москвы и закрытие API для доступа к электронным дневникам. А кроме нее: автоматизация тестирования, чистая архитектура, мобильный фрод и многое другое.



Сначала они воруют, а когда ты побеждаешь, то тебя убивают

В начале лета 2017 года, я спросил ЕМП: «Будут ли какие-то изменения в методах или в функционале?», а мне ответили, что пока они не знают. 1 августа я получил письмо о том, что АПИ больше не будет и попросили меня сообщить моим пользователям, что теперь надо пользоваться госуслугами.

Заблуждения Clean Architecture

На первый взгляд, Clean Architecture – довольно простой набор рекомендаций к построению приложений. Но и я, и многие мои коллеги, сильные разработчики, осознали эту архитектуру не сразу. А в последнее время в чатах и интернете я вижу всё больше ошибочных представлений, связанных с ней. Этой статьёй я хочу помочь сообществу лучше понять Clean Architecture и избавиться от распространенных заблуждений.

Разработка интерфейса приложения для пожизненного использования на примере мобильного дневника диабета

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

Дайджест доступен и в виде рассылки. Подписаться вы можете тут.

iOS


Android


Windows


Разработка


Аналитика, маркетинг и монетизация


Устройства, IoT, AI



< Предыдущий дайджест. Если у вас есть другие интересные материалы или вы нашли ошибку — пришлите, пожалуйста, в почту.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335534/


Optimization Unity3d UI by GPU (for example minimap) или создаем миникарту без дополнительных камер и спрайтов

Воскресенье, 13 Августа 2017 г. 15:55 + в цитатник
Всем привет.

«Если можешь что-то посчитать на GPU, делай это»
// Конечно в рамках разумного

image
VS
image
Обращаем внимание на разницу в фпс.
Начну пожалуй с предыстории. Один из наших программистов, решил проверить UI на предмет падения фпс. И мы нашли интересную зависимость, при отключении миникарты фпс поднимался в процентном соотношении. Интересно. Нужно решать проблему. Сразу напишу что про атласы и различные пулы, мы пробывали. И тогда я решил занятся этим вопросом более детально. И тут первая мысль, которая меня посетила, UI использует материал, значит можно все перенести на ГПУ, начнем.

Начал гуглить, находил много скажем так, нехороших решений, например таких как
Не делайте так, даже если хотите сделать быстро
Почему выше пример плохо, можно найти в комментариях. Но для ленивых я перечислю.
streeter12 4 июля 2016 в 14:51
Данный метод несмотря на свою простоту имеет явные недостатки.
1. Низкая производительность.
2. Для добавления новых меток надо создавать новые сферы (лишний хлам в префабах).
3. Добавление новых типов меток и их фильтров для различных игроков сильно затруднено.
4. Для смены внешнего вида метки необходимо создавать меш!
5. Лишние объекты на каждой сцене => лишняя сложность => сложнее разработка и поддержка.
6. Сложно тестировать => больше возможных багов (с учетом 3).

7. Реалтаймовая замена типа.
8. Нужно захломлять сцену фейковой подложкой.
9. Как быть с теми кто не должен учитывать вращение? обнулять в Update или таскать через tranform.position.
и т.д.

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

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

В общем, начнем реализацию.


static const int MaxCount = 256;
float4 _Targets[MaxCount];
float _WorldSizeX;
float _WorldSizeY;


Такие поля я объявил в шейдере. Поставил максимально 256 на мапе точек. Остальные будут отбрасываться. float _WorldSizeX поле решило проблему с преобразованиями, я как есть решил кормить Vector3 в шейдер. Не хитрыми махинациями, я получил индексы и нормализованные координаты, и тем самым получил практически то что хотел.
Нюанс: GC считает y иначе снизу в врех. Я конечно сделал обратные значения, чтобы левая верхняя была индексом 0. Но потом решил отказаться от этой затеи. Лучше решать это путем верхнего слоя. Например кодом.

И того у меня были заняты 3 поля из Vector4 (X Z W) чем я занял Y будет позже.

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

Для примера как примерно выглядит скрипт для шейдера.
protected virtual void Update ()
{
for (int i = 0; i < list.Length; i++)
{
list[i] = new Vector4(0, 0, 0, -1);
}

for (int i = 0; i < list.Length && i < targets.Count ; i++)
{
list[i] = new Vector4(targets[i].x *k.x, targets[i].y, targets[i].z*k.y, targets[i].w);
}
image.GetModifiedMaterial(image.material).SetVectorArray("_Targets", list);
}


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

image

Результат работы. Как было видно, я добавил еще и вращение. Но для того что-бы его использовать следует сделать иконки на достаточном расстоянии друг от друга в атласе. Я не стал выдумывать каких то сложных алгоритмов отсечения в шейдере, гораздо проще все это сделать текстурой.

Тот самый свободный Y, я решил использовать в качестве угла. С ним пришлось отдельно повозится, причина была выше, а именно инверсия координаты Y. Именно из-за вращения я решил использовать все как есть.
fixed a = _Targets[i].y;
fixed resultX = tX;
fixed resultY = tY;
if (a != 0)
{
fixed x0 = minX + (sizeX * 0.5);
fixed y0 = minY + (sizeY * 0.5);
resultX = x0 + (tX - x0) * cos(a) - (tY - y0) * sin(a);
resultY = y0 + (tY - y0) * cos(a) + (tX - x0) * sin(a);
}


Вот теперь шейдер готов. Последний штрих, это смягчение альфа канала на границах. Я использовал интерполяцию, от афьфа до барьера. Так картинка получилась качественее.

Выводы

Тест для статьи. Я не стал делать полное соответствие. Это бессмысленно, и поэтому контент отличается. Но чтобы понять разницу, этого более чем предостаточно. Следующим этапом, когда дойдут руки, на аналогичную систему хочу перевести тайловый инвентарь.

Выложу тестовые коды и результаты профайлера.
Заполнение спрайтами.
protected virtual void Start ()
{
tests = new List();
float x = 10;
float y = 10;

for (int i = 0; i < 256; i++)
{
int idx = Random.Range(0, prefabs.Count);

GameObject obj = Instantiate(prefabs[idx].gameObject);
RectTransform t = obj.GetComponent();
t.SetParent(parent);
t.localPosition = new Vector3(startPosition.x + x, startPosition.y + y, 0);
if (useRotation)
{
t.rotation = Quaternion.Euler(0, 0, Random.Range(0, 360));
}
x += step;
if (i % 20 == 0)
{
y += step;
x = 0;
}
tests.Add(t);
}
}

Заполнения через прокси класс для шейдера
item.type = Random.Range(0, 64); означает тип иконки
void Start () {
tests = new List();
float x = 10;
float z = 10;

for (int i = 0; i < 256; i++)
{
Item2 item = new Item2();
item.position = new Vector3(x, 0, z);
if (useRotation)
{
item.rotation = Random.Range(0, 360);
}
item.type = Random.Range(0, 64);
x += step;
if (i % 20 == 0)
{
z += step;
x = 0;
}
tests.Add(item);

}
}


Профайлер для шейдера (на сцене еще 3 куба)
image
Профайлер для спрайтов (на сцене еще 3 image в качестве префабов)
image

и напоследок пример с маской.
image

P.S.
"Если можешь что-то посчитать на GPU, делай это"
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335524/


Метки:  

[Из песочницы] Получение записей телефонной книги с мобильного телефона без дисплея

Воскресенье, 13 Августа 2017 г. 15:17 + в цитатник
Это скорее развлекательная история для тех, кто так же, как и я, не является специалистом в телефонах и восстановлении данных.

Однажды сотрудник принёс телефон Fly Ezzy Trendy 3.

image
(по его версии телефон начал первый, но сотрудник оказался сильнее)

Осознание того, что телефонная книга осталась у побеждённого наступило не сразу. И через некоторое время сотрудник обратился в ближайший сервис с просьбой извлечь телефонную книгу.

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

Проверили, вроде включается. Во всяком случае, загорается подсветка клавиатуры.

Лезем в гугл и узнаём, что телефон работает на процессоре MediaTek MT6261D. Смотрим какие сервисные программы используются для работы с устройствами на базе таких процессоров. Выбираем MAUI META и заодно качаем пакет драйверов для устройств на базе процессоров MTK.

Тут я заинтересовался названием программы
И если META это Mobile Engineering Testing Architecture, как следует из найденой документации, то с MAUI не совсем понятно.

Нагуглил вот такие варианты:

  • Most Advanced User Interface
  • Multimodal Affective User Interface
  • Multimedia Application User Interface

Но это всё лирика.

Ставим драйвера, запускаем MauiMETA, выбираем FAT Editor, подключаем телефон и видим файловую систему телефона.

image

Копируем на компьютер содержимое NVRAM\NVD_DATA и начинаем усиленно его изучать. Оказалось, что нам нужны всего два файла MP0C_003 (здесь хранятся имена) и MP0H_006 (здесь хранятся номера телефонов). Так же, там был файл с смсками MPA3_001, но они нам были не нужны.

Будем использовать Frhed. Определяем размеры записей в этих файлах.

image
(вот здесь хорошо видно, где заканчиваются данные и идут пустые записи)

Формат записи в файле с именами:

  • 44 байта на одну запись
  • 2 последних байта под контрольную сумму
  • сами записи в UTF-16LE

Формат записи в файле с телефонами:

  • 38 байт на одну запись
  • каждая запись начинается с 0x00 0x00 0x01 0x00
  • 2 последних байта под контрольную сумму
  • сами записи в ASCII

Теперь было бы неплохо как-то автоматизировать процесс извлечения из файлов номеров телефонов и имён. Поскольку под рукой был вёб-сервер и я немного могу в php, набросал скрипт, закинул файлы на сервер. В результате получился файл с записями такого вида: имя контакта, номер телефона. Дальше импортировали в таблицу.

Довольный владелец телефона ушёл со списком своих контактов в закат.

P.S. Из спортивного интереса решил проверить действительно ли последние два байта каждой записи являются какой-то контрольной суммой и если да, то каков её алгоритм.

Читал про 16-битные контрольные суммы типа CRC16 и безуспешно подбирал ту, которая подошла бы мне. Запарился и решил использовать «помощь зала» «звонок другу», который предположил, что всё намного проще и там может использоваться простая сумма всех байт записи.

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

Простенький скрипт



Проверка контрольных сумм на примере файла с именами



Надеюсь, что кому-то эта заметка поможет, а кого-то просто позабавит.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335528/


Метки:  

[Из песочницы] Безопасный OpenVPN на VPS за несколько минут

Воскресенье, 13 Августа 2017 г. 12:12 + в цитатник

Введение


В связи с последними событиями и возможной блокировкой публичных VPN сервисов созрела идея облегчить жизнь людям и сделать скрипт для быстрой установки OpenVPN со всеми настройками и легким выпуском сертификатов.

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

Скрипт создавался для машин с CentOS 7.x или Ubuntu Server 17.x, использование на Ubuntu 16.x. возможно, но там OpenVPN 2.3.x в репозиториях. При необходимости можно добавить другие дистрибутивы, но обычно при покупке VPS можно выбрать систему и это не так важно.

Скрипт написан на bash за пару часов, возможны ошибки и наверняка что-то можно было реализовать проще и лучше.

Запускайте скрипт на свежей машине, он перезаписывает правила iptables и конфигурацию OpenVPN. И да, в правилах iptables разрешен порт ssh 22, если вы поменяли его на другой, не забудьте поменять порт в скрипте.

Особенности


  1. По умолчанию рекомендуется cipher AES-256-GCM (что достаточно безопасно на данный момент);
  2. По умолчанию используется auth SHA256 (вместо дефолтного SHA1);
  3. По умолчанию для OpenVPN 2.4.x используется tls-crypt (что усложняет обнаружение трафика OpenVPN);
  4. По умолчанию использует Google DNS и блокировку локальных DNS (setenv opt block-outside-dns) для предотвращения DNS Leak;
  5. Создаются все нужные правила в iptables и ip6tables;
  6. Есть поддержка IPv6.

Как пользоваться


Использовать скрипт очень просто, скачайте файл openvpnsetup.sh на ваш VPS, дайте ему права на запуск chmod +x openvpnsetup.sh и запустите ./openvpnsetup.sh. В результате вы получаете настроенный сервер, готовый к работе на выбранном вами порту.

В папке /etc/openvpn создается скрипт newclient.sh, который нужен для создания файлов конфигурации клиентской части, использовать его так же просто — ./newclient.sh clientname. Результатом будет файл /etc/openvpn/bundles/clientname.ovpn, который сразу можно использовать на клиенте, просто положите его в папку config (в случае использования на Windows) на вашей машине.

Если вы захотите пересоздать сервер, просто удалите все из папки /etc/openvpn и запустите скрипт заново (естественно, клиентские сертификаты надо будет перевыпустить).

Советы по выбору VPS для OpenVPN


  1. В первую очередь смотрим на цену, можно найти предложения за $3-4 в месяц, что дешевле многих VPN сервисов;
  2. Выбирайте VPS ближе к вам географически, если хотите иметь приемлемую скорость через VPN. Чем меньше пинг от вас до VPS, тем лучше скорость;
  3. Выбирайте минимальную конфигурацию. OpenVPN не использует больше одного ядра и может работать на 256MB памяти. Минимального дискового пространства в 3-5GB так же вполне достаточно;
  4. Некоторые VPS ограничены по трафику, но обычно это 1TB в месяц, если вы планируете использовать больше, рассмотрите другие тарифные планы;
  5. Перед тем как оформить заказ на VPS, уточните разрешена ли загрузка торрентов (при условии, что они вам нужны, конечно);
  6. Так же можно уточнить включены ли TUN/TAP устройства в системе. В скрипте есть проверка на это, но лучше уточнить до покупки, возможно их и нельзя будет включить через поддержку провайдера VPS;
  7. Наличие IPv6 адреса, скрипт позволяет настроить сервер с поддержкой IPv6 и возможно вы захотите иметь возможность посещать IPv6 ресурсы через VPN.

Скрипт доступен на GitHub.

Бонус: результат проверки анонимности на 2ip.ru:

image

Проверка на WITCH?:

image

Проверка на DNS Leak:

image
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335516/


Метки:  

Использование ImGui с SFML для создания инструментов для разработки игр

Воскресенье, 13 Августа 2017 г. 09:52 + в цитатник

Привет, Хабр!
Данная статья — вольный перевод моей статьи на русский с некоторыми небольшими изменениями и улучшениями. Хотелось бы показать как просто и полезно использовать ImGui с SFML. Приступим.



Введение


При разработке игр очень важно иметь хорошие инструменты для создания контента (редактор уровней, ресурсов и пр.), а также для дебаггинга. Наличие данных вещей повышает производительность и креативность. Гораздо проще отлавливаются баги и исправляются уже найденные: легко выводить значение различных переменных, а также создавать виджеты для их изменения, чтобы посмотреть, что происходит с игрой при определённых их значениях. Виджеты для изменения переменных также очень полезны для полировки геймплея. Например, можно легко изменять скорость передвижения персонажей, скорость перезарядки оружия и пр.


Вот какие инструменты я создал с помощью ImGui для своей игры:



Редактор уровней



Консоль Lua



Редактор анимаций


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


ImGui и концепция immediate GUI


Immediate mode GUI немного отличается от классической методики программирования интерфейсов, которая называется retained mode GUI. ImGui виджеты создаются и рисуются в каждом кадре игрового цикла. Сами виджеты не хранят внутри себя своё состояние, либо хранят абсолютно минимальный необходимый минимум, который обычно скрыт от программиста.


В отличие от того же Qt, где для создания кнопки нужно создавать объект QPushButton, а затем связывать с ней какую-нибудь функцию-callback, вызываемую при нажатии, в ImGui всё делается гораздо проще. В коде достаточно написать:


if (ImGui::Button("Some Button")) {
    ... // код, вызываемый при нажатии кнопки
}

Данный код должен вызываться в каждой итерации игрового цикла, в которой эта кнопка должна быть доступна пользователю.


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


Советую посмотреть вот это видео Кейси Муратори про ImGui, если вы хотите узнать чуть больше о данной методике.


Итак, каковы же достоинства ImGui?


  • MIT-лицензия
  • Очень быстрая и занимает мало памяти
  • Постоянно обновляется и расширяется
  • Почти не производит динамическую аллокацию/деалокаццию (и это можно контролировать, устанавливая каким образом ImGui будет получать необходимую память)
  • Очень портабельна: есть множество биндингов для различных библиотек и платформ
  • Очень легко расширяется путём создания новых виджетов на основе существующих, либо написанных с нуля.

Настройка


Итак, начнём


  1. Создайте простую программу на SFML, которая показывает пустое окно. Если вы раньше этим не занимались, то можете воспользоваться туториалом.
  2. Скачайте ImGui.
  3. Скачайте ImGui SFML биндинг и положите его в папку, в которую скачали ImGui.
    Важно: добавьте содержимое imconfig-SFML.h в imconfig.h
  4. Добавьте папку ImGui в include директории вашего проекта
  5. Добавьте следующие файлы в билд вашего проекта:


    • imgui.cpp
    • imgui_draw.cpp
    • imgui-SFML.cpp
    • imgui_demo.cpp

  6. Если вы будете получать ошибки линковки, то залинкуйте OpenGL к своему проекту.

Вот небольшой пример кода, который создаёт окошко ImGui и позволяет менять цвет заднего фона и заголовок окна. Объяснения того, что происходит, будут далее.


#include "imgui.h"
#include "imgui-sfml.h"

#include Graphics/RenderWindow.hpp>
#include System/Clock.hpp>
#include Window/Event.hpp>

int main()
{
    sf::RenderWindow window(sf::VideoMode(640, 480), "");
    window.setVerticalSyncEnabled(true);
    ImGui::SFML::Init(window);

    sf::Color bgColor;
    float color[3] = { 0.f, 0.f, 0.f };

    // здесь мы будем использовать массив char. Чтобы использовать
    // std::string нужно сделать действия, описанные во второй части
    char windowTitle[255] = "ImGui + SFML = <3";
    window.setTitle(windowTitle);

    sf::Clock deltaClock;
    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            ImGui::SFML::ProcessEvent(event);

            if (event.type == sf::Event::Closed) {
                window.close();
            }
        }

        ImGui::SFML::Update(window, deltaClock.restart());

        ImGui::Begin("Sample window"); // создаём окно

        // Инструмент выбора цвета
        if (ImGui::ColorEdit3("Background color", color)) {
            // код вызывается при изменении значения, поэтому всё
            // обновляется автоматически
            bgColor.r = static_cast(color[0] * 255.f);
            bgColor.g = static_cast(color[1] * 255.f);
            bgColor.b = static_cast(color[2] * 255.f);
        }

        ImGui::InputText("Window title", windowTitle, 255);

        if (ImGui::Button("Update window title")) {
            // этот код выполняется, когда юзер жмёт на кнопку
            // здесь можно было бы написать 
            // if(ImGui::InputText(...))
            window.setTitle(windowTitle);
        }
        ImGui::End(); // end window

        window.clear(bgColor); // заполняем окно заданным цветом
        ImGui::SFML::Render(target);
        window.display();
    }

    ImGui::SFML::Shutdown();
}

Вы должны увидеть что-то вроде этого:



Попробуйте изменить что-нибудь. Если кликнуть два раза на одно из полей RGB, то можно ввести соответствующее значение. Если одно из полей потянуть, то можно плавно изменять текущее введённое значение. Поле ввода позволяет изменить заголовок окна после нажатия на кнопку.



Отлично, теперь разберёмся как всё работает.


ImGui инициализируется вызовом ImGui::SFML::Init, при вызове в функцию передаётся ссылка на окно sf::RenderWindow. В этот момент также создаётся стандартный шрифт, который будет использоваться в дальнейшем. (см. раздел Fonts how-to в описании imgui-sfml, чтобы увидеть как использовать другие шрифты).


При выходе из программы важно вызывать функцию ImGui::SFML::Shutdown, которая освобождает ресурсы, которые использует ImGui.


В игровом цикле ImGui имеет две фазы: обновление и рендеринг.


Обновление состоит из обработки событий, обновления состояния ImGui и обновления/создания виджетов. Обработка событий происходит через вызов ImGui::SFML::ProcessEvent. ImGui обрабатывает события клавиатуры, мыши, изменения фокуса и размера окна. Обновление состояния ImGui производится в ImGui::SFML::Update, в неё передаётся delta time (время между двумя обновлениями), который ImGui использует для обновления состояния виджетов (например, для анимации). Также в данной функции вызывается ImGui::NewFrame, после вызова которой уже можно создавать новые виджеты.


Рендеринг ImGui осуществляется вызовом ImGui::SFML::Render. Очень важно создавать/обновлять виджеты между вызовами ImGui::SFML::Update и ImGui::SFML::Render, иначе ImGui будет ругаться на нарушение состояния.


Если вы рендерите реже, чем обновляете ввод и игру, то в конце каждой итерации вашего update необходимо также вызывать ImGui::EndFrame:


while (gameIsRunning) {
    while (updateIsNeeded()) {
        updateGame(dt);
        ImGui::SFML::Update(window, dt);
        ImGui::EndFrame();
    }
    renderGame();
}

Виджеты создаются путём вызова соответствующих функций (например, ImGui::InputInt или ImGui::Button). Если вызвать ImGui::ShowTestWindow, то можно увидеть много примеров использования ImGui, весь код можно найти в imgui_demo.cpp.


Полезные перегрузки функций для SFML


Для SFML в биндинге были созданы некоторые перегрузки функций, например в ImGui::Image и ImGui::ImageButton можно кидать sf::Sprite и sf::Texture, также можно легко рисовать линии и прямоугольники вызовом DrawLine, DrawRect и DrawRectFilled.


Заключение


Вот такая библиотека: проста в использовании и настройке, и очень полезна для создания инструментов и дебагинга. Приятного использования!


P.S. Если возникнет интерес, то могу перевести и вторую часть туториала, которая рассказывает про использование ImGui с современным C++ и стандартной библиотекой. Советую обратить на статью внимание тем, кто решит использовать (или уже использует) ImGui: она показывает как просто решать основные проблемы ImGui и делать всё проще и безопаснее, чем это делается в C++03.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335512/


Метки:  

Проклятые Земли — Улучшаем бег и опыт с напарниками

Суббота, 12 Августа 2017 г. 20:20 + в цитатник



Многие играли в эту замечательную игру. Интересный сюжет, хорошая музыка, неплохой геймплей. Только есть пара моментов, которые мне не нравятся. Бег персонажей очень ограниченный, буквально несколько секунд, а запас сил восстанавливается долго. Система начисления опыта не стимулирует брать напарников, потому что опыт распределяется на всех поровну, и лучше бегать одному, чтобы забирать весь опыт себе. Возьмем отладчик и попробуем это исправить.



Нам понадобятся ArtMoney, IDA, Hiew.

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

Запускаем IDA, загружаем файл game.exe, ждем пока завершится анализ.
Запускаем стартер, убираем настройку «Полноэкранный режим».
Запускаем игру в отладчике, загружаем сохранение где уже можно взять напарника.
Берем напарника, выходим на карту.




Бег



Смотрим, сколько запаса сил у персонажа. Здесь это 54.
Запускаем ArtMoney. Ищем это значение. Тип «С точкой 4 байта».
Пробегаем сколько-нибудь и отсеиваем новое значение.
Повторяем сколько нужно, у меня сразу осталось одно значение.


Пробуем заморозить, если запас сил при беге восстанавливается, значит нашли правильно. Только надо снять потом.
Включаем режим бега. Ставим игру на паузу (пробел).

Ставим на этот адрес брейкпойнт на запись в IDA. Перед этим обязательно надо сделать 'Pause process', иначе случаются вылеты.
Переключаемся на игру, отправляем персонажа в какую-нибудь точку, снимаем с паузы.

Брейкпойнт срабатывает.
.text:00548315 loc_548315:
.text:00548315                 fld     dword ptr [edi+14h]
.text:00548318                 fld     dword ptr [edi+18h]
.text:0054831B                 fmul    ds:dbl_73F088
.text:00548321                 fsubp   st(1), st
.text:00548323                 fst     dword ptr [edi+14h]
.text:00548326 >               fcomp   ds:flt_73B858
.text:0054832C                 fnstsw  ax
.text:0054832E                 test    ah, 1
.text:00548331                 jz      short loc_548388


Запись происходит в инструкции fst dword ptr [edi+14h]. Можно поставить Operand type — Floating point на этот адрес и на соседний [edi+18h].


Судя по значениям, там хранится:
[edi+14h] — текущее значение
[edi+18h] — максимальное значение

Видно, что максимальный запас сил персонажа умножается на константу dbl_73F088, и результат вычитается из текущего значения. Поэтому все персонажи бегают одинаково.
.rdata:0073F088 dbl_73F088      dq 6.666666666666666e-3
; 6.666666666666666e-3 = 0.006666666666666666 = 1/150

То есть персонаж может пробежать примерно 150 «шагов», но это не совсем шаги, которые видно на анимации, потому что вычитание происходит гораздо чаще. Полный запас сил расходуется за 9-10 секунд, значит вычитание вызывается 15 — 16.66 раз в секунду.
Скорее всего это зеленые точки, обозначающие путь.



В байтах эта константа записывается так:
4E 1B E8 B4 81 4E 7B 3F


Открываем game.exe в Hiew и переходим на адрес ".73F088".


Других констант с таким значением нет, ссылка на нее есть только в рассмотренном коде.

Можно ее поменять на любое значение, которое вам нужно. Я сделал себе в 3 раза меньше.
(1/150)/3 = 1/450 = 0.0022222222222222222

Для конвертации float/double в hex-представление можно воспользоваться онлайн-конвертером, например этим.

для справки
0.0066666666666666667 - 0x3F7B4E81B4E81B4F
0.006666666666666666  - 0x3F7B4E81B4E81B4E
0.0022222222222222222 - 0x3F623456789ABCDF
0.002222222222222222  - 0x3F623456789ABCDE


Получается красивое число 0x3F623456789ABCDF
DF BC 9A 78 56 34 62 3F

Заменяем, сохраняем, запускаем. Вот, так гораздо лучше.



Опыт



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

Смотрим, сколько опыта отображается у персонажа. Это можно сделать в режиме между картами. У меня это 116.


Ищем это значение. Здесь нужен тип «Целое 4 байта».

Это не исходная переменная, а вычисляемое значение, приведенное к int. Сам опыт хранится во float, но там другое значение, об этом ниже.

Теперь можно с кем-нибудь подраться. Выходить с карты чтобы посмотреть опыт нельзя, перезагружаться тоже, потому что память выделяется заново, и при повторном заходе на карту будут другие адреса. Надо прибавлять в уме. При этом надо учитывать округление. То есть, если за противника дается 5 очков опыта, и в команде 2 персонажа, то на экране будет отображаться опыт 2, но прибавлять надо 2.5 и брать целую часть.



После пары повторений у меня осталось 6 значений, которые изменяются синхронно.


Попробуем поставить брейкпойнты на запись на каждый адрес. Не забываем про «Pause process».

Подходит первый адрес. Остальные срабатывают по rep movsd.
.text:00522D00                 fld     dword ptr [ebx+700h]
.text:00522D06                 fadd    dword ptr [ebx+4]
.text:00522D09                 fsub    dword ptr [ebx+8]
.text:00522D0C                 fstp    [ebp+var_10]
.text:00522D0F                 fld     [ebp+var_10]
.text:00522D12                 fistp   [ebp+var_C]
.text:00522D15                 mov     edx, [ebp+var_C]
.text:00522D18                 mov     [edi+8], edx
.text:00522D1B >               mov     eax, [ebx+10h]
.text:00522D1E                 mov     [ebp+var_10], eax


Он срабатывает при беге или при бое любого из персонажей. Поэтому здесь лучше управлять только одним, а не группой, чтобы не путаться. В ebx находится адрес объекта персонажа. В [ebx+700h] находится 0.

Посмотрим значения.


387 — 271 = 116
Можно предположить, что это полученный и потраченный опыт, а текущий рассчитывается как их разность.

Уберем этот брейкпойнт и поставим новый на [ebx+4].

Выбираем всю группу и нападаем на противника.



Брейкпойнт срабатывает до того, как это будет видно на экране.

.text:005239D7                 fld     ds:dbl_73E128
.text:005239DD                 fld     dword ptr [esi+20h]
.text:005239E0                 fsub    ds:flt_73E124
.text:005239E6                 call    __CIpow
.text:005239EB                 fmul    [ebp+arg_4]
.text:005239EE                 fadd    dword ptr [esi+700h]
.text:005239F4                 fcom    ds:flt_73B858
.text:005239FA                 fst     dword ptr [esi+700h]
.text:00523A00                 fnstsw  ax
.text:00523A02                 test    ah, 41h
.text:00523A05                 jnz     short loc_523A19
.text:00523A07                 fadd    dword ptr [esi+4]
.text:00523A0A                 mov     dword ptr [esi+700h], 0
.text:00523A14                 fstp    dword ptr [esi+4]
.text:00523A17 >               jmp     short loc_523A1B


В [esi+4] новое значение опыта. В [ebp+arg_4] число 2.0. За молодого кабана дают 4.0, значит деление находится до вызова функции.

Выходим из функции через Ctrl+F7. Это обертка, выходим еще раз.

Смотрим чуть выше, там находится такой код.
.text:00591521 loc_591521:
.text:00591521                 fild    [ebp+var_18]
.text:00591524                 xor     esi, esi
.text:00591526                 cmp     eax, edi
.text:00591528                 mov     [ebp+var_14], esi
.text:0059152B                 fdivr   [ebp+arg_4]
.text:0059152E                 fstp    [ebp+arg_4]
.text:00591531                 jle     short loc_5915A5
.text:00591533                 jmp     short loc_591537


Также деление на число участников есть выше:
.text:00591324                 fdiv    [ebp+var_18]

Но я не нашел, когда выполняется этот код. Там трогать не будем.

Поставим брейкпойнт на 00591521 и поучаствуем в битве еще раз.
fdivr делит аргумент на st(0) и результат записывает в st(0): st(0) = arg / st(0). В st(0) находится значение [ebp+var_18], в котором находится 2 — число персонажей. В [ebp+arg_4] находится 4.0 — опыт за противника. При выполнении миссии начисление тоже происходит здесь.

Теперь через Hiew можно убрать код для деления. Из-за особенностей fdivr заменяем на nop все 3 команды (9 байт).
// было
.00591521: DB45E8                         fild        d,[ebp][-018]
.00591524: 33F6                           xor         esi,esi
.00591526: 3BC7                           cmp         eax,edi
.00591528: 8975EC                         mov         [ebp][-014],esi
.0059152B: D87D0C                         fdivr       d,[ebp][00C]
.0059152E: D95D0C                         fstp        d,[ebp][00C]
.00591531: 7E72                           jle        .0005915A5
.00591533: EB02                           jmps       .000591537

// стало
.00591521: 909090                         nop
.00591524: 33F6                           xor         esi,esi
.00591526: 3BC7                           cmp         eax,edi
.00591528: 8975EC                         mov         [ebp][-014],esi
.0059152B: 909090                         nop
.0059152E: 909090                         nop
.00591531: 7E72                           jle        .0005915A5
.00591533: EB02                           jmps       .000591537


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

Поехали!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335498/


Метки:  

Проклятые Земли — Улучшаем бег и опыт с напарниками

Суббота, 12 Августа 2017 г. 20:20 + в цитатник



Многие играли в эту замечательную игру. Интересный сюжет, хорошая музыка, неплохой геймплей. Только есть пара моментов, которые мне не нравятся. Бег персонажей очень ограниченный, буквально несколько секунд, а запас сил восстанавливается долго. Система начисления опыта не стимулирует брать напарников, потому что опыт распределяется на всех поровну, и лучше бегать одному, чтобы забирать весь опыт себе. Возьмем отладчик и попробуем это исправить.



Нам понадобятся ArtMoney, IDA, Hiew.

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

Запускаем IDA, загружаем файл game.exe, ждем пока завершится анализ.
Запускаем стартер, убираем настройку «Полноэкранный режим».
Запускаем игру в отладчике, загружаем сохранение где уже можно взять напарника.
Берем напарника, выходим на карту.




Бег



Смотрим, сколько запаса сил у персонажа. Здесь это 54.
Запускаем ArtMoney. Ищем это значение. Тип «С точкой 4 байта».
Пробегаем сколько-нибудь и отсеиваем новое значение.
Повторяем сколько нужно, у меня сразу осталось одно значение.


Пробуем заморозить, если запас сил при беге восстанавливается, значит нашли правильно. Только надо снять потом.
Включаем режим бега. Ставим игру на паузу (пробел).

Ставим на этот адрес брейкпойнт на запись в IDA. Перед этим обязательно надо сделать 'Pause process', иначе случаются вылеты.
Переключаемся на игру, отправляем персонажа в какую-нибудь точку, снимаем с паузы.

Брейкпойнт срабатывает.
.text:00548315 loc_548315:
.text:00548315                 fld     dword ptr [edi+14h]
.text:00548318                 fld     dword ptr [edi+18h]
.text:0054831B                 fmul    ds:dbl_73F088
.text:00548321                 fsubp   st(1), st
.text:00548323                 fst     dword ptr [edi+14h]
.text:00548326 >               fcomp   ds:flt_73B858
.text:0054832C                 fnstsw  ax
.text:0054832E                 test    ah, 1
.text:00548331                 jz      short loc_548388


Запись происходит в инструкции fst dword ptr [edi+14h]. Можно поставить Operand type — Floating point на этот адрес и на соседний [edi+18h].


Судя по значениям, там хранится:
[edi+14h] — текущее значение
[edi+18h] — максимальное значение

Видно, что максимальный запас сил персонажа умножается на константу dbl_73F088, и результат вычитается из текущего значения. Поэтому все персонажи бегают одинаково.
.rdata:0073F088 dbl_73F088      dq 6.666666666666666e-3
; 6.666666666666666e-3 = 0.006666666666666666 = 1/150

То есть персонаж может пробежать примерно 150 «шагов», но это не совсем шаги, которые видно на анимации, потому что вычитание происходит гораздо чаще. Полный запас сил расходуется за 9-10 секунд, значит вычитание вызывается 15 — 16.66 раз в секунду.
Скорее всего это зеленые точки, обозначающие путь.



В байтах эта константа записывается так:
4E 1B E8 B4 81 4E 7B 3F


Открываем game.exe в Hiew и переходим на адрес ".73F088".


Других констант с таким значением нет, ссылка на нее есть только в рассмотренном коде.

Можно ее поменять на любое значение, которое вам нужно. Я сделал себе в 3 раза меньше.
(1/150)/3 = 1/450 = 0.0022222222222222222

Для конвертации float/double в hex-представление можно воспользоваться онлайн-конвертером, например этим.

для справки
0.0066666666666666667 - 0x3F7B4E81B4E81B4F
0.006666666666666666  - 0x3F7B4E81B4E81B4E
0.0022222222222222222 - 0x3F623456789ABCDF
0.002222222222222222  - 0x3F623456789ABCDE


Получается красивое число 0x3F623456789ABCDF
DF BC 9A 78 56 34 62 3F

Заменяем, сохраняем, запускаем. Вот, так гораздо лучше.



Опыт



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

Смотрим, сколько опыта отображается у персонажа. Это можно сделать в режиме между картами. У меня это 116.


Ищем это значение. Здесь нужен тип «Целое 4 байта».

Это не исходная переменная, а вычисляемое значение, приведенное к int. Сам опыт хранится во float, но там другое значение, об этом ниже.

Теперь можно с кем-нибудь подраться. Выходить с карты чтобы посмотреть опыт нельзя, перезагружаться тоже, потому что память выделяется заново, и при повторном заходе на карту будут другие адреса. Надо прибавлять в уме. При этом надо учитывать округление. То есть, если за противника дается 5 очков опыта, и в команде 2 персонажа, то на экране будет отображаться опыт 2, но прибавлять надо 2.5 и брать целую часть.



После пары повторений у меня осталось 6 значений, которые изменяются синхронно.


Попробуем поставить брейкпойнты на запись на каждый адрес. Не забываем про «Pause process».

Подходит первый адрес. Остальные срабатывают по rep movsd.
.text:00522D00                 fld     dword ptr [ebx+700h]
.text:00522D06                 fadd    dword ptr [ebx+4]
.text:00522D09                 fsub    dword ptr [ebx+8]
.text:00522D0C                 fstp    [ebp+var_10]
.text:00522D0F                 fld     [ebp+var_10]
.text:00522D12                 fistp   [ebp+var_C]
.text:00522D15                 mov     edx, [ebp+var_C]
.text:00522D18                 mov     [edi+8], edx
.text:00522D1B >               mov     eax, [ebx+10h]
.text:00522D1E                 mov     [ebp+var_10], eax


Он срабатывает при беге или при бое любого из персонажей. Поэтому здесь лучше управлять только одним, а не группой, чтобы не путаться. В ebx находится адрес объекта персонажа. В [ebx+700h] находится 0.

Посмотрим значения.


387 — 271 = 116
Можно предположить, что это полученный и потраченный опыт, а текущий рассчитывается как их разность.

Уберем этот брейкпойнт и поставим новый на [ebx+4].

Выбираем всю группу и нападаем на противника.



Брейкпойнт срабатывает до того, как это будет видно на экране.

.text:005239D7                 fld     ds:dbl_73E128
.text:005239DD                 fld     dword ptr [esi+20h]
.text:005239E0                 fsub    ds:flt_73E124
.text:005239E6                 call    __CIpow
.text:005239EB                 fmul    [ebp+arg_4]
.text:005239EE                 fadd    dword ptr [esi+700h]
.text:005239F4                 fcom    ds:flt_73B858
.text:005239FA                 fst     dword ptr [esi+700h]
.text:00523A00                 fnstsw  ax
.text:00523A02                 test    ah, 41h
.text:00523A05                 jnz     short loc_523A19
.text:00523A07                 fadd    dword ptr [esi+4]
.text:00523A0A                 mov     dword ptr [esi+700h], 0
.text:00523A14                 fstp    dword ptr [esi+4]
.text:00523A17 >               jmp     short loc_523A1B


В [esi+4] новое значение опыта. В [ebp+arg_4] число 2.0. За молодого кабана дают 4.0, значит деление находится до вызова функции.

Выходим из функции через Ctrl+F7. Это обертка, выходим еще раз.

Смотрим чуть выше, там находится такой код.
.text:00591521 loc_591521:
.text:00591521                 fild    [ebp+var_18]
.text:00591524                 xor     esi, esi
.text:00591526                 cmp     eax, edi
.text:00591528                 mov     [ebp+var_14], esi
.text:0059152B                 fdivr   [ebp+arg_4]
.text:0059152E                 fstp    [ebp+arg_4]
.text:00591531                 jle     short loc_5915A5
.text:00591533                 jmp     short loc_591537


Также деление на число участников есть выше:
.text:00591324                 fdiv    [ebp+var_18]

Но я не нашел, когда выполняется этот код. Там трогать не будем.

Поставим брейкпойнт на 00591521 и поучаствуем в битве еще раз.
fdivr делит аргумент на st(0) и результат записывает в st(0): st(0) = arg / st(0). В st(0) находится значение [ebp+var_18], в котором находится 2 — число персонажей. В [ebp+arg_4] находится 4.0 — опыт за противника. При выполнении миссии начисление тоже происходит здесь.

Теперь через Hiew можно убрать код для деления. Из-за особенностей fdivr заменяем на nop все 3 команды (9 байт).
// было
.00591521: DB45E8                         fild        d,[ebp][-018]
.00591524: 33F6                           xor         esi,esi
.00591526: 3BC7                           cmp         eax,edi
.00591528: 8975EC                         mov         [ebp][-014],esi
.0059152B: D87D0C                         fdivr       d,[ebp][00C]
.0059152E: D95D0C                         fstp        d,[ebp][00C]
.00591531: 7E72                           jle        .0005915A5
.00591533: EB02                           jmps       .000591537

// стало
.00591521: 909090                         nop
.00591524: 33F6                           xor         esi,esi
.00591526: 3BC7                           cmp         eax,edi
.00591528: 8975EC                         mov         [ebp][-014],esi
.0059152B: 909090                         nop
.0059152E: 909090                         nop
.00591531: 7E72                           jle        .0005915A5
.00591533: EB02                           jmps       .000591537


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

Поехали!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335498/


Метки:  

[Перевод] Переосмысление PID 1. Часть 2

Суббота, 12 Августа 2017 г. 16:48 + в цитатник
В продолжение первой части…

Распараллеливание служб сокетов


Этот вид синхронизации при загрузке приводит к опоследовательности (последовательный запуск служб) существенной части процесса загрузки. Не было бы круто если бы мы могли избавиться от цены синхронизации и опоследовательности? Что ж, мы можем на самом деле избавиться. Для этого, нам необходимо понять, что на самом деле службы (демоны) требуют друг от друга, и почему их запуск откладывается. Для традиционных демонов (служб) Unix, есть только один ответ на этот вопрос: они ждут до тех пор, пока демон предоставляющий свои службы не будет готов принимать соединения. Обычно это AF_UNIX сокет в файловой системе, но это может таже быть AF_INET сокет. Для примера: клиенты D-Bus ждут /var/run/dbus/system_bus_socket, чтобы сконнектиться к нему, клиенты syslog ждут /dev/log, клиенты CUPS ждут /var/run/cups/cups.sock и NFS точки монтирования ждут /var/run/rpcbind.sock и порт IP портмаппера и т.д. А теперь задумайтесь об этом, на самом деле есть только одна вещь чего ждут остальные.

Так как это лежит в основе того, что следует далее, позвольте мне сказать это снова, но другими словами: если вы запускаете syslog и различных клиентов syslog одновременно, что произойдет в схеме обозначенной выше, так это то, что сообщения от клиентов будут добавлены в буфер /dev/log. До тех пор, пока буфер не переполнится, клиенты не должны ждать пока syslog закончит загрузку, они вытащат все сообщения из очереди и обработают их. Другой пример: мы запускаем D-Bus и несколько клиентов одновременно. Если отправлен синхронный запрос к шине, следовательно, будет ожидаться ответ, также синхронно, и произойдет то, что клиент будет заблокирован, тем не менее только один единственный этот клиент (пославший синхронный запрос) и только до тех пора пока D-Bus не отловит запрос и не обработает его.

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

Хороший момент, чтобы сделать паузу и пойти налить себе еще кофейка, и будьте уверены, что далее нас ждет материал еще более интересный.

Но в начале, давайте проясним несколько вещей: это какая-то новая логика? Нет, конечно же нет. Наиболее многообещающая система, которая работает как эта — это launchd от Apple: в MacOS прослушивание сокетов и запуск всех демонов делает launchd. Следовательно, все службы (демоны) могут запуститься параллельно и без необходимости в сконфигурированных зависимостях. И это в действительности изобретательное решение и главная причина почему MacOS предоставляет фантачтическое время загрузки. Я крайне рекомендую это видео где ребята из launchd объясняют, что и как они это делают. К сожалению, эта идея не была признана за пределами кампуса Apple.

Идея, на самом деле старее чем launchd. До launchd многоуважаемый inetd работал в схожем стиле: сокеты в основном создаются в демонах, которые запускает реальную службу (основной так сказать функционал), передавая дескриптор файла сокета функции exec(). Тем не менее, фокус inetd в основном был направлен не на локальные службы и демоны, а на службы Интернета (поздние реализации также поддерживают AF_UNIX сокет). inetd не был инструментом распараллеливания процесса загрузки системы или даже полезным для того чтобы получить правильные зависимости.

Для сокетов TCP inetd в основном использовался следующим образом: для каждого входящего соединения создавался новый экземпляр демона. Это означает, что для каждого соединения инициализировался и создавался новый процесс, что не назовешь рецептом для высокопроизводительных серверов. Тем не менее, с самого начала inetd также поддерживал и другой режим работы, где единственный демон создавался на первом соединении, и этот единственный экземпляр принимал последующие соединения (вот для чего был опции wait/nowait в inetd.conf, и эта была очень плохо документированная опция, к сожалению). Запуск демона на каждое соединение послужило для inted плохой репутацией как слишком медленного. Но это не совсем справедливо.

Распараллеливание служб шины


В современных службах в Linux прослеживается тенденция предоставлять службы через D-Bus взамен плоским AF_UNIX сокетам. Теперь, вопрос в следующем, можем ли мы применить ту же логику распараллеливания логики, как и для традиционных служб сокетов? Да, мы можем, D-Bus уже имеет все нужные механизм для этого: используя шину активации служба может быть запущена, когда в первый раз к ней обратятся. Шина активации также предоставляет нам минимальные функции синхронизации на каждый запрос, необходимый для запуска провайдеров и потребителей служб D-Bus одновременно: если мы хотим запустить Avahi одновременно с CUPS (отвлеченная заметка: CUPS использует Avahi чтобы обнаружить mDNS/DNS-SD принтеры), тогда мы можем запустить их одновременно, и если CUPS быстрее чем Avahi через логику активации шины, D-Bus поставит в очередь запрос до тех пора, пока Avahi занят, тем чтобы установить свое сервисное имя на шине.

Итак, таким образом: сокет-основанные активация службы и шина-основанная активация службы, вместе позволяют нам запустить все демоны параллельно, без какой-либо дальнейшей синхронизации. Активация также позволяет нам делать «ленвиую» загрузку сервисов: если служба (демон) редко используется, мы можем просто запустить его на первое обращение к сокету или к имени на шине, вместо того чтобы запустить его во время загрузки системы.

И если это не великолепно, то тогда я не знаю, что великолепно!

Продолжение следует…
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335488/


Метки:  

[Перевод] Переосмысление PID 1. Часть 2

Суббота, 12 Августа 2017 г. 16:48 + в цитатник
В продолжение первой части…

Распараллеливание служб сокетов


Этот вид синхронизации при загрузке приводит к опоследовательности (последовательный запуск служб) существенной части процесса загрузки. Не было бы круто если бы мы могли избавиться от цены синхронизации и опоследовательности? Что ж, мы можем на самом деле избавиться. Для этого, нам необходимо понять, что на самом деле службы (демоны) требуют друг от друга, и почему их запуск откладывается. Для традиционных демонов (служб) Unix, есть только один ответ на этот вопрос: они ждут до тех пор, пока демон предоставляющий свои службы не будет готов принимать соединения. Обычно это AF_UNIX сокет в файловой системе, но это может таже быть AF_INET сокет. Для примера: клиенты D-Bus ждут /var/run/dbus/system_bus_socket, чтобы сконнектиться к нему, клиенты syslog ждут /dev/log, клиенты CUPS ждут /var/run/cups/cups.sock и NFS точки монтирования ждут /var/run/rpcbind.sock и порт IP портмаппера и т.д. А теперь задумайтесь об этом, на самом деле есть только одна вещь чего ждут остальные.

Так как это лежит в основе того, что следует далее, позвольте мне сказать это снова, но другими словами: если вы запускаете syslog и различных клиентов syslog одновременно, что произойдет в схеме обозначенной выше, так это то, что сообщения от клиентов будут добавлены в буфер /dev/log. До тех пор, пока буфер не переполнится, клиенты не должны ждать пока syslog закончит загрузку, они вытащат все сообщения из очереди и обработают их. Другой пример: мы запускаем D-Bus и несколько клиентов одновременно. Если отправлен синхронный запрос к шине, следовательно, будет ожидаться ответ, также синхронно, и произойдет то, что клиент будет заблокирован, тем не менее только один единственный этот клиент (пославший синхронный запрос) и только до тех пора пока D-Bus не отловит запрос и не обработает его.

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

Хороший момент, чтобы сделать паузу и пойти налить себе еще кофейка, и будьте уверены, что далее нас ждет материал еще более интересный.

Но в начале, давайте проясним несколько вещей: это какая-то новая логика? Нет, конечно же нет. Наиболее многообещающая система, которая работает как эта — это launchd от Apple: в MacOS прослушивание сокетов и запуск всех демонов делает launchd. Следовательно, все службы (демоны) могут запуститься параллельно и без необходимости в сконфигурированных зависимостях. И это в действительности изобретательное решение и главная причина почему MacOS предоставляет фантачтическое время загрузки. Я крайне рекомендую это видео где ребята из launchd объясняют, что и как они это делают. К сожалению, эта идея не была признана за пределами кампуса Apple.

Идея, на самом деле старее чем launchd. До launchd многоуважаемый inetd работал в схожем стиле: сокеты в основном создаются в демонах, которые запускает реальную службу (основной так сказать функционал), передавая дескриптор файла сокета функции exec(). Тем не менее, фокус inetd в основном был направлен не на локальные службы и демоны, а на службы Интернета (поздние реализации также поддерживают AF_UNIX сокет). inetd не был инструментом распараллеливания процесса загрузки системы или даже полезным для того чтобы получить правильные зависимости.

Для сокетов TCP inetd в основном использовался следующим образом: для каждого входящего соединения создавался новый экземпляр демона. Это означает, что для каждого соединения инициализировался и создавался новый процесс, что не назовешь рецептом для высокопроизводительных серверов. Тем не менее, с самого начала inetd также поддерживал и другой режим работы, где единственный демон создавался на первом соединении, и этот единственный экземпляр принимал последующие соединения (вот для чего был опции wait/nowait в inetd.conf, и эта была очень плохо документированная опция, к сожалению). Запуск демона на каждое соединение послужило для inted плохой репутацией как слишком медленного. Но это не совсем справедливо.

Распараллеливание служб шины


В современных службах в Linux прослеживается тенденция предоставлять службы через D-Bus взамен плоским AF_UNIX сокетам. Теперь, вопрос в следующем, можем ли мы применить ту же логику распараллеливания логики, как и для традиционных служб сокетов? Да, мы можем, D-Bus уже имеет все нужные механизм для этого: используя шину активации служба может быть запущена, когда в первый раз к ней обратятся. Шина активации также предоставляет нам минимальные функции синхронизации на каждый запрос, необходимый для запуска провайдеров и потребителей служб D-Bus одновременно: если мы хотим запустить Avahi одновременно с CUPS (отвлеченная заметка: CUPS использует Avahi чтобы обнаружить mDNS/DNS-SD принтеры), тогда мы можем запустить их одновременно, и если CUPS быстрее чем Avahi через логику активации шины, D-Bus поставит в очередь запрос до тех пора, пока Avahi занят, тем чтобы установить свое сервисное имя на шине.

Итак, таким образом: сокет-основанные активация службы и шина-основанная активация службы, вместе позволяют нам запустить все демоны параллельно, без какой-либо дальнейшей синхронизации. Активация также позволяет нам делать «ленвиую» загрузку сервисов: если служба (демон) редко используется, мы можем просто запустить его на первое обращение к сокету или к имени на шине, вместо того чтобы запустить его во время загрузки системы.

И если это не великолепно, то тогда я не знаю, что великолепно!

Продолжение следует…
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335488/


Метки:  

Информационная экономика: почему стоимость технологических компаний так высока

Суббота, 12 Августа 2017 г. 15:00 + в цитатник
Инвесторы всё чаще предпочитают вкладываться в нематериальные активы (intangible investment), а не в компании, производящие промышленное оборудование, оснащение и другие материальные ценности (tangibles). Компании в США владеют нематериальными активами, общая сумма которых превышает $8 трлн. Это почти половина стоимости индекса S&P 500 на момент проведения исследования.

image

Нематериальные активы состоят из прав, отношений и интеллектуальной собственности. Но среди них выделяется особая форма – разработки, появившиеся в результате расходов на НИОКР. Они составляют основной доход любой современной организации. Это сбивает с толку инвестора. Дело в том, что действующий механизм не может адекватно связать такие блага с ценой компани.
Способы ведения бухучета и методы оценки, использующиеся в XXI веке, плохо подходят для нематериальных активов и искажают их стоимость.

Издание The Market Mogul опубликовало интересную статью о том, почему капитализации технологических компаний в последние годы находятся на очень высоких уровнях. Мы подготовили адаптированную версию этого материала.

Как формируется оценка бизнеса в 21 веке


Учетные политики, которые организации используют для управления своими экономическими процессами, не подходят для современных нематериальных активов. Это создает причину для беспокойства при оценки акций. Регулирующие инструменты, такие как МСФО, признают нематериальные активы только в момент совершения сделки, например покупки. Такой стандарт не учитывает нематериальные активы компании, которые формируют ее акционерную стоимость. Значимость возрастает, когда из $89 трлн цены предприятия $30,1 трлн считается «нераскрытой стоимостью».

Кроме того, классификация затрат может неточно отображать определенные показатели, непосредственно влияющие на оценку, такие как дисконтированный денежный поток. Основными расходами, которые стимулируют информационное развитие, являются НИОКР. В бухгалтерской отчетности они отражаются как операционные затраты, хотя по своей сути создают долгосрочные экономические выгоды. Если вручную переконфигурировать отчетность о прибылях и убытках и проводить расходы на НИОКР в качестве капитальных затрат, создается более точное представление о доходах компании.

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

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

Основная проблема


Фундаментальная проблема заключается в неспособности ценового механизма работать с чем-то неограниченным. Современная экономическая система не учитывает, что затраты на данные, необходимые для производства товаров, будут стремиться к нулю. Компаниям ничего не стоит воссоздать несколько строк кода и скопировать информацию. Следовательно, естественные рыночные механизмы не могут создать цену.

Хотя некоторые исследования показывают, что данные потребляют вычислительную мощность при удалении «одного бита», для фиксации значения требуется более надежная система.

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

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

Вывод


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

В результате внедряются новые установки, которые позволяют фиксировать внешние факторы, связанные с нематериальными активами. Это обеспечивает ясность, но не создает надежной системы ценообразования, которую требует текущая бизнес-среда. Может ли следующий длительный экономический цикл обеспечить то, что требуется?

Другие материалы по теме финансов и фондового рынка от ITinvest:


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335492/


Метки:  

[Из песочницы] Разработка telegram бота с использованием Spring

Суббота, 12 Августа 2017 г. 14:43 + в цитатник

Пишите телеграмм ботов? Ваша производительность разработки желает лучшего? Ищите чего-то нового? Тогда прошу под кат.



Идея заключается в следующем: слямзить архитектуру spring mvc и перенести на telegram api.
Выглядеть должно как-то так:


@BotController
public class SimpleOkayController {
    @BotRequestMapping(value = "/ok")
    public SendMessage ok(Update update) {
        return new SendMessage()
                .setChatId(update.getMessage().getChatId())
                .setText("okay bro, okay!");
    }
}

или


Пример с бинами
@BotController
public class StartController {
    @Autowired
    private Filter shopMenu;

    @Autowired
    private PayTokenService payTokenService;

    @Autowired
    private ItemService itemService;

    @BotRequestMapping("/shop")
    public SendMessage generateInitMenu(Update update) {
            return  new SendMessage()
                    .setChatId(update.getMessage().getChatId().toString())
                    .setText("Товары моего магазинчика!")
                    .setReplyMarkup(shopMenu.getSubMenu(0L, 4L, 1L)); // <--
    }

    @BotRequestMapping(value = "/buyItem", method = BotRequestMethod.EDIT)
    public List bayItem(Update update) {
        ....................
        Item item = itemService.findById(id); // <--

        return Arrays.asList(new EditMessageText()
                .setChatId(update.getMessage().getChatId())
                .setMessageId(update.getMessage().getMessageId())
                .setText("Подтвердите ваш выбор, в форме ниже"),

                new SendInvoice()
                        .setChatId(Integer.parseInt(update.getMessage().getChatId().toString()))
                        .setDescription(item.getDescription())
                        .setTitle(item.getName())
                        .setProviderToken(payTokenService.getPayToken())
                        ........................
                        .setPrices(item.getPrice())
        );
    }

}

Это даёт следующие преимущества:


  • Не надо писать кастомную логику для выбора обработчика сообщения от пользователя
  • Возможность инжектить разлиные бины в наш @BotController
  • Как следствие из предыдущих двух пунктов — существенное сокращение объемов кода
  • Потенциально (хотя я этого еще не сделал) аргументы кастомного метода обработчика, могут быть выражены в виде тех аргументов, которые действительно нужны!
  • Возможность создавать серьезные энтерпрайз решения, используя spring

Давайте теперь посмотрим как это можно завести в нашем проекте


Аннотации
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface BotController {
    String[] value() default {};
}

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface BotRequestMapping {
    String[] value() default {};
    BotRequestMethod[] method() default {BotRequestMethod.MSG};
}

Создаем свой контейнер обработчиков в виде обычной HashMap


Контейнер
public class BotApiMethodContainer {
    private static final Logger LOGGER = Logger.getLogger(BotApiMethodContainer.class);

    private Map controllerMap;

    public static BotApiMethodContainer getInstanse() {
        return Holder.INST;
    }

    public void addBotController(String path, BotApiMethodController controller) {
        if(controllerMap.containsKey(path)) throw new BotApiMethodContainerException("path " + path + " already add");
        LOGGER.trace("add telegram bot controller for path: " +  path);
        controllerMap.put(path, controller);
    }

    public BotApiMethodController getBotApiMethodController(String path) {
        return controllerMap.get(path);
    }

    private BotApiMethodContainer() {
        controllerMap = new HashMap<>();
    }

    private static class Holder{
        final static BotApiMethodContainer INST = new BotApiMethodContainer();
    }
}

В контейнере будем хранить контроллеры обертки (для пары @BotController и @BotRequestMapping)


Контроллер обертка
public abstract class BotApiMethodController {
    private static final Logger LOGGER = Logger.getLogger(BotApiMethodController.class);

    private Object bean;
    private Method method;
    private Process processUpdate;

    public BotApiMethodController(Object bean, Method method) {
        this.bean = bean;
        this.method = method;

        processUpdate = typeListReturnDetect() ? this::processList : this::processSingle;
    }

    public abstract boolean successUpdatePredicate(Update update);

    public List process(Update update) {
        if(!successUpdatePredicate(update)) return null;

        try {
            return processUpdate.accept(update);
        } catch (IllegalAccessException | InvocationTargetException e) {
            LOGGER.error("bad invoke method", e);
        }

        return null;
    }

    boolean typeListReturnDetect() {
        return List.class.equals(method.getReturnType());
    }

    private List processSingle(Update update) throws InvocationTargetException, IllegalAccessException {
        BotApiMethod botApiMethod = (BotApiMethod) method.invoke(bean, update);
        return botApiMethod != null ? Collections.singletonList(botApiMethod) : new ArrayList<>(0);
    }

    private List processList(Update update) throws InvocationTargetException, IllegalAccessException {
        List botApiMethods = (List) method.invoke(bean, update);
        return botApiMethods != null ? botApiMethods : new ArrayList<>(0);
    }

    private interface Process{
        List accept(Update update) throws InvocationTargetException, IllegalAccessException;
    }
}

Теперь когда у нас есть данная кодовая база возникает вопрос: как Spring заставить автоматически наполнять контейнер, чтобы мы могли им пользоваться?


Для этого реализуем специальный бин — BeanPostProcessor. Это дает возможность отлавливать бины во время их инициализации. Наши контроллеры имеют scope по умолчанию — синглтон, значит инициализироваться они будут со стартом контекста!


TelegramUpdateHandlerBeanPostProcessor
@Component
public class TelegramUpdateHandlerBeanPostProcessor implements BeanPostProcessor, Ordered {
    private static final Logger LOGGER = Logger.getLogger(TelegramUpdateHandlerBeanPostProcessor.class);

    private BotApiMethodContainer container = BotApiMethodContainer.getInstanse();
    private Map botControllerMap = new HashMap<>();

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class

https://habrahabr.ru/post/335490/


Метки:  

[Из песочницы] Пишем расширения для PHP 7 на C++

Суббота, 12 Августа 2017 г. 10:29 + в цитатник
Мне приходилось писать расширения для того, чтобы воспользоваться функциями C++ библиотек в коде PHP. Ещё, одно тяжёлое расширение портировал с 5й версии на 7ю.

Если загуглить документацию на тему написания расширений для PHP, то, в основном, это будут тексты до 2014 года, актуальные для версии 5. Сам сайт php.net предоставляет обрывчатые и устаревшие сведения, а то, что удаётся найти в их wiki, опять про 5ю версию. Максимум, что удалось найти на офф сайте, это скудный ман по миграции уже написанных расширений.
В итоге, единственным более или менее понятным маном по написанию расширений для меня оказался исходный код PHP, которым я и руководствовался при написании и миграции расширений.

В самом деле, API PHP так поменялся, что даже подробнейшие статьи, такие как Wrapping C++ Classes in a PHP Extension не особенно то помогают при написании расширений под PHP 7.

В данной статье рассматривается работа под Linux, у меня Kubuntu. Для винды нужно писать другие config файлы, а так как в проде винду не ставят и расширять PHP под виндой — дело не благодарное, я в этом не разбирался.

Что нужно


php-dev, gcc, исходные коды php. Комбо по установке всего, что нужно для сборки из исходных кодов, можно легко нагуглить.

Определяем лица на фотографиях


Для определения лиц используем библиотеку OpenCV, тестировалось на версиях >=2.3.1

Отправная точка для создания расширений — утилита ext_skel. Она позволяет создать болванку для нового расширения. Мы будем править код, который получился после выполнения этой команды.

Нужно перейти в папку /ext исходных кодов PHP и от туда выполнить
ext_skel с указанием имени нового расширения:

./ext_skel --extname=phpcv

После этого вновь созданную папку phpcv можно перенести куда-то в более удобное место. Нам от туда нужны папка tests и файлы config.m4, php_phpcv.h и phpcv.c. Файл phpcv.c сразу переименуем в phpcv.cpp.

config.m4


Это конфигурационный файл, используемый утилитой phpize для подготовки нашего расширения к компилированию.

Файл представляет собой эдакий bash скрипт с использованием специальных макросов. Макросы эти определены в файлах acinclude.m4 и aclocal.m4 в исходном коде php, и написаны на языке из скобочек и знаков препинания. На самом деле достаточно почитать коменты, которые начинаются со строк «dnl» и будет более или менее понятно, что эти макросы делают.

Удаляем лишнее, правим код под наши нужды.

config.m4

PHP_ARG_ENABLE(phpcv, whether to enable phpcv support,
    [  --enable-phpcv           Enable phpcv support])

if test "$PHP_PHPCV" != "no"; then
    PHP_REQUIRE_CXX()

    SEARCH_PATH="/usr/local /usr /opt/local"
    SEARCH_FOR="/include/opencv2/opencv.hpp"

    if test -r $PHP_PHPCV/$SEARCH_FOR; then
        CV_DIR=$PHP_PHPCV
    else
        AC_MSG_CHECKING([for opencv in default path])
        for i in $SEARCH_PATH ; do
            if test -r $i/$SEARCH_FOR; then
                CV_DIR=$i
                AC_MSG_RESULT(found in $i)
                break
            fi
        done
    fi

    if test -z "$CV_DIR"; then
        AC_MSG_RESULT([not found])
        AC_MSG_ERROR([Please reinstall the OpenCV distribution])
    fi

    AC_CHECK_HEADER([$CV_DIR/include/opencv2/objdetect/objdetect.hpp], [], AC_MSG_ERROR('opencv2/objdetect/objdetect.hpp' header not found))
    AC_CHECK_HEADER([$CV_DIR/include/opencv2/highgui/highgui.hpp], [], AC_MSG_ERROR('opencv2/highgui/highgui.hpp' header not found))

    PHP_ADD_LIBRARY_WITH_PATH(opencv_objdetect, $CV_DIR/lib, PHPCV_SHARED_LIBADD)
    PHP_ADD_LIBRARY_WITH_PATH(opencv_highgui, $CV_DIR/lib, PHPCV_SHARED_LIBADD)
    PHP_ADD_LIBRARY_WITH_PATH(opencv_imgproc, $CV_DIR/lib, PHPCV_SHARED_LIBADD)

    PHP_SUBST(PHPCV_SHARED_LIBADD)
    PHP_NEW_EXTENSION(phpcv, phpcv.cpp, $ext_shared,, -std=c++0x -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
fi



PHP_ARG_ENABLE — настраиваем флаг, с помощью которого расширение можно включить или выключить в процессе сборки PHP из исходных кодов.
PHP_REQUIRE_CXX() — необходимо, если мы собираемся использовать C++
Далее, код на bash для поиска opencv.
AC_CHECK_HEADER — проверяем наличие необходимых нам заголовочных файлов
PHP_ADD_LIBRARY_WITH_PATH — подключаем shared библиотеки
PHP_SUBST — это необходимо для формирования make файла
PHP_NEW_EXTENSION — тут указано имя расширения, перечислены *.cpp файлы, которые участвуют в процессе сборки, указаны флаги компилятора.

php_phpcv.h


php_phpcv.h
#ifndef PHP_PHPCV_H
#define PHP_PHPCV_H

#define PHP_PHPCV_EXTNAME "phpcv"
#define PHP_PHPCV_VERSION "0.2.0"

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

extern "C" {
#include "php.h"
#include "ext/standard/info.h"
}

#ifdef ZTS
#include "TSRM.h"
#endif

extern zend_module_entry phpcv_module_entry;
#define phpext_phpcv_ptr &phpcv_module_entry

#if defined(ZTS) && defined(COMPILE_DL_PHPCV)
ZEND_TSRMLS_CACHE_EXTERN();
#endif

#endif	/* PHP_PHPCV_H */



Тут стоит обратить внимание на конструкцию

extern "C" { ... }
Это нужно для совместимости нашего C++ кода с C кодом PHP.

phpcv.cpp


Вот тут, наконец-то, будет C++ код.
Для нахождения лиц будем использовать метод cv::CascadeClassifier::detectMultiScale().

Расширение будет предоставлять единственную функцию, вот её прототип:

/**
 * @see cv::CascadeClassifier::detectMultiScale()
 * @param string $imgPath
 * @param string $cascadePath
 * @param double $scaleFactor
 * @param int $minNeighbors
 *
 * @return array
 */
function cv_detect_multiscale($imgPath, $cascadePath, $scaleFactor, $minNeighbors) {
}

phpcv.cpp
#include "php_phpcv.h"
#include objdetect/objdetect.hpp>
#include highgui/highgui.hpp>
#include imgproc/imgproc.hpp>

PHP_MINFO_FUNCTION(phpcv) {
    php_info_print_table_start();
    php_info_print_table_header(2, "phpcv support", "enabled");
    php_info_print_table_end();
}

PHP_FUNCTION(cv_detect_multiscale) {
    char *imgPath = NULL, *cascadePath = NULL;
    long imgPathLen, cascadePathLen, minNeighbors;
    double scaleFactor, minWidth, minHeight;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssdl", &imgPath, &imgPathLen, &cascadePath, &cascadePathLen, &scaleFactor, &minNeighbors) == FAILURE) {
        RETURN_FALSE;
    }

    // Read Image
    cv::Mat image;
    image = cv::imread(imgPath, CV_LOAD_IMAGE_GRAYSCALE);
    if (image.empty()) {
        RETURN_FALSE;
    }
    equalizeHist(image, image);

    //min size for detected object, discarding objects smaller than this
    minWidth = image.size().width / 10;
    minHeight = image.size().height / 10;

    // Load Face cascade (.xml file)
    cv::CascadeClassifier faceCascade;
    if (!faceCascade.load(cascadePath)) {
        RETURN_FALSE;
    }

    // Detect faces
    std::vector faces;
    faceCascade.detectMultiScale(image, faces, scaleFactor, minNeighbors, 0, cv::Size(minWidth, minHeight));

    array_init(return_value);

    // Build array to return
    for ( int i = 0; i < faces.size(); i++ ) {
        // Now we have: faces[i].x faces[i].y faces[i].width faces[i].height
        zval face;
        array_init(&face);
        add_assoc_long(&face, "x", faces[i].x);
        add_assoc_long(&face, "y", faces[i].y);
        add_assoc_long(&face, "w", faces[i].width);
        add_assoc_long(&face, "h", faces[i].height);

        add_next_index_zval(return_value, &face);
    }
}

const zend_function_entry phpcv_functions[] = {
    PHP_FE(cv_detect_multiscale, NULL)
    PHP_FE_END
};

zend_module_entry phpcv_module_entry = {
    STANDARD_MODULE_HEADER,
    PHP_PHPCV_EXTNAME,
    phpcv_functions,
    NULL,
    NULL,
    NULL,
    NULL,
    PHP_MINFO(phpcv),
    PHP_PHPCV_VERSION,
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_PHPCV
#ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE();
#endif
ZEND_GET_MODULE(phpcv)
#endif



PHP_MINFO_FUNCTION — добавляет сведения о нашем расширении в вывод phpinfo()
PHP_FUNCTION(cv_detect_multiscale) — код нашей функции. В ней после получения входных параметров с помощью zend_parse_parameters идёт код С++. С помощью библиотеки opencv находим лица и формируем выходной массив с координатами найденных лиц.
zend_function_entry — тут перечисляются функции, которые предоставляются расширением.
zend_module_entry — стандартная конструкция, структура, описывающая наше расширение. Несколько NULL подряд — это вместо методов, которые выполняются при инициализации и shutdown расширения и запроса, нам просто нечего делать во время этих фаз.

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

tests


Тест расширения состоит из кода, который что-то выводит и проверки вывода. Файлы *.phpt с тестами помещаются в папку tests

002.phpt
--TEST--
Test face detection
--SKIPIF--

--FILE--

--EXPECT--
face detection works



Cборка и тестирование


Собрать:

phpize && ./configure && make

Протестировать:

make test

DateTime Pattern Generator


Теперь рассмотрим расширение, которое предоставляет класс, оборачивающий C++ класс.
Добавим недостающий функционал в набор классов расширения intl. Зачем это нужно: https://blog.ksimka.io/a-long-journey-to-formatting-a-date-without-a-year-internationally-with-php/#header. Если коротко, стандартное расширение intl не предоставляет возможности интернационально формировать дату без года, то есть «February 10» или «10 февраля». Это расширение фиксит эту проблему.

В системе должна быть установлена библиотека ICU. В Debian-like системах можно поставить пакет libicu-dev.

config.m4


config.m4

PHP_ARG_ENABLE(intl-dtpg, whether to enable intl-dtpg support,
[  --enable-intl-dtpg           Enable intl-dtpg support])

if test "$PHP_INTL_DTPG" != "no"; then
  PHP_SETUP_ICU(INTL_DTPG_SHARED_LIBADD)
  PHP_SUBST(INTL_DTPG_SHARED_LIBADD)
  PHP_REQUIRE_CXX()

  PHP_NEW_EXTENSION(intl_dtpg, intl_dtpg.cpp, $ext_shared,,-std=c++0x $ICU_INCS -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
fi



На этот раз всё очень лаконично. Так как нам нужна библиотека ICU и расширение intl тоже требует её присутствия, то мы просто позаимствовали макрос PHP_SETUP_ICU из intl расширения.
В PHP_NEW_EXTENSION $ICU_INCS — специфичные для ICU флаги.

intl_dtpg.h


intl_dtpg.h
#ifndef INTL_DTPG_H
#define INTL_DTPG_H

#include 
#include 
#include dtptngen.h>

extern "C" {
#include "php.h"
#include "ext/standard/info.h"
}

extern zend_module_entry intl_dtpg_module_entry;
#define phpext_intl_dtpg_ptr &intl_dtpg_module_entry

#define INTL_DTPG_VERSION "1.0.0"

#ifdef ZTS
#include "TSRM.h"
#endif

typedef struct {
    DateTimePatternGenerator *dtpg;
    UErrorCode status;
    zend_object zo;
} IntlDateTimePatternGenerator_object;

static inline IntlDateTimePatternGenerator_object *php_intl_datetimepatterngenerator_fetch_object(zend_object *obj) {
    return (IntlDateTimePatternGenerator_object *)((char*)(obj) - XtOffsetOf(IntlDateTimePatternGenerator_object, zo));
}

#if defined(ZTS) && defined(COMPILE_DL_INTL_DTPG)
ZEND_TSRMLS_CACHE_EXTERN()
#endif

#endif /* INTL_DTPG_H */



Тут мы определяем структуру IntlDateTimePatternGenerator_object. В ней хранится указатель на объект DateTimePatternGenerator из библиотеки ICU, переменная для хранения статуса и объект zend_object, который представляет собой PHP класс. Это такая обёртка для C++ класса. API PHP оперирует объектом zend_object, а мы завернули его в струкутуру и всегда имеем доступ к тому, что находится «по соседству» с zend_object.

Ниже мы видим определение inline функции для извлечения структуры IntlDateTimePatternGenerator_object имея объект zend_object. Под капотом мы делаем в памяти шаг назад от начала zend_object на размер нашей структуры минус размер zend_object. Таким образом мы оказываемся как раз в начале структуры, указатель на которую нам и возвращается. Такой хитрый способ подсмотрен в исходных кодах расширения intl.

intl_dtpg.cpp


Расширение будет предоставлять класс следующей структуры:

class IntlDateTimePatternGenerator
{
    /**
     * @param string $locale
     */
    public function __construct(string $locale) {}

    /**
     * Return the best pattern matching the input skeleton.
     * It is guaranteed to have all of the fields in the skeleton.
     *
     * @param string $skeleton The skeleton is a pattern containing only the variable fields.
     *           For example, "MMMdd" and "mmhh" are skeletons.
     * @return string The best pattern found from the given skeleton.
     */
    public function findBestPattern(string $skeleton) {}
}

intl_dtpg.cpp
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "intl_dtpg.h"
#include ustdio.h>
#include smpdtfmt.h>

zend_class_entry *IntlDateTimePatternGenerator_ce;
zend_object_handlers IntlDateTimePatternGenerator_object_handlers;

/* {{{ IntlDateTimePatternGenerator_objects_dtor */
static void IntlDateTimePatternGenerator_object_dtor(zend_object *object)
{
    zend_objects_destroy_object(object);
}
/* }}} */

/* {{{ IntlDateTimePatternGenerator_objects_free */
void IntlDateTimePatternGenerator_object_free(zend_object *object)
{
    IntlDateTimePatternGenerator_object *dtpgo = php_intl_datetimepatterngenerator_fetch_object(object);

    zend_object_std_dtor(&dtpgo->zo);

    dtpgo->status = U_ZERO_ERROR;
    if (dtpgo->dtpg) {
        delete dtpgo->dtpg;
        dtpgo->dtpg = nullptr;
    }
}
/* }}} */

/* {{{ IntlDateTimePatternGenerator_object_create */
zend_object *IntlDateTimePatternGenerator_object_create(zend_class_entry *ce)
{
    IntlDateTimePatternGenerator_object* intern;

    intern = (IntlDateTimePatternGenerator_object*)ecalloc(1,
                        sizeof(IntlDateTimePatternGenerator_object) + zend_object_properties_size(ce));

    zend_object_std_init(&intern->zo, ce);
    object_properties_init(&intern->zo, ce);
    intern->dtpg = nullptr;
    intern->status = U_ZERO_ERROR;

    intern->zo.handlers = &IntlDateTimePatternGenerator_object_handlers;

    return &intern->zo;
}
/* }}} */

/* {{{ proto void IntlDateTimePatternGenerator::__construct(string $locale)
 * IntlDateTimePatternGenerator object constructor.
 */
PHP_METHOD(IntlDateTimePatternGenerator, __construct)
{
    zend_string *locale;
    zval *object;
    IntlDateTimePatternGenerator_object* dtpg = nullptr;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &locale) == FAILURE) {
        return;
    }

    object = getThis();
    dtpg = php_intl_datetimepatterngenerator_fetch_object(Z_OBJ_P(object));

    dtpg->status = U_ZERO_ERROR;
    dtpg->dtpg = DateTimePatternGenerator::createInstance(Locale(ZSTR_VAL(locale)), dtpg->status);
}
/* }}} */

/* {{{ proto string IntlDateTimePatternGenerator::findBestPattern(string $skeleton)
 * Return the best pattern matching the input skeleton.
 */
PHP_METHOD(IntlDateTimePatternGenerator, findBestPattern)
{
    zend_string *skeleton;
    zval *object;
    IntlDateTimePatternGenerator_object* dtpg = nullptr;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &skeleton) == FAILURE) {
        return;
    }

    object = getThis();
    dtpg = php_intl_datetimepatterngenerator_fetch_object(Z_OBJ_P(object));

    UnicodeString pattern = dtpg->dtpg->getBestPattern(UnicodeString(ZSTR_VAL(skeleton)), dtpg->status);

    std::string s;
    pattern.toUTF8String(s);
    RETURN_STRING(s.c_str());
}
/* }}} */

ZEND_BEGIN_ARG_INFO_EX(arginfo_findBestPattern, 0, 0, 1)
    ZEND_ARG_INFO(0, skeleton)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, 0, 1)
    ZEND_ARG_INFO(0, locale)
ZEND_END_ARG_INFO()

const zend_function_entry IntlDateTimePatternGenerator_functions[] = {
    PHP_ME(IntlDateTimePatternGenerator, __construct, arginfo___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
    PHP_ME(IntlDateTimePatternGenerator, findBestPattern, arginfo_findBestPattern, ZEND_ACC_PUBLIC)
    PHP_FE_END
};

/* {{{ PHP_MINIT_FUNCTION
 */
PHP_MINIT_FUNCTION(intl_dtpg)
{
    zend_class_entry ce;
    INIT_CLASS_ENTRY(ce, "IntlDateTimePatternGenerator", IntlDateTimePatternGenerator_functions);
    ce.create_object = IntlDateTimePatternGenerator_object_create;
    IntlDateTimePatternGenerator_ce = zend_register_internal_class(&ce);

    memcpy(&IntlDateTimePatternGenerator_object_handlers, zend_get_std_object_handlers(),
        sizeof IntlDateTimePatternGenerator_object_handlers);
    IntlDateTimePatternGenerator_object_handlers.offset =
                    XtOffsetOf(IntlDateTimePatternGenerator_object, zo);
    IntlDateTimePatternGenerator_object_handlers.clone_obj = NULL; //no clone support
    IntlDateTimePatternGenerator_object_handlers.dtor_obj = IntlDateTimePatternGenerator_object_dtor;
    IntlDateTimePatternGenerator_object_handlers.free_obj = IntlDateTimePatternGenerator_object_free;

    if(!IntlDateTimePatternGenerator_ce) {
        zend_error(E_ERROR, "Failed to register IntlDateTimePatternGenerator class");
        return FAILURE;
    }

    return SUCCESS;
}
/* }}} */

/* {{{ PHP_MSHUTDOWN_FUNCTION
 */
PHP_MSHUTDOWN_FUNCTION(intl_dtpg)
{
    return SUCCESS;
}
/* }}} */

/* {{{ PHP_MINFO_FUNCTION
 */
PHP_MINFO_FUNCTION(intl_dtpg)
{
    php_info_print_table_start();
    php_info_print_table_header(2, "intl_dtpg support", "enabled");
    php_info_print_table_header(2, "intl_dtpg version", INTL_DTPG_VERSION);
    php_info_print_table_end();
}
/* }}} */

/* {{{ intl_dtpg_functions[]
 */
const zend_function_entry intl_dtpg_functions[] = {
    PHP_FE_END
};
/* }}} */

/* {{{ intl_dtpg_module_entry
 */
zend_module_entry intl_dtpg_module_entry = {
    STANDARD_MODULE_HEADER,
    "intl_dtpg",
    intl_dtpg_functions,
    PHP_MINIT(intl_dtpg),
    PHP_MSHUTDOWN(intl_dtpg),
    NULL,
    NULL,
    PHP_MINFO(intl_dtpg),
    INTL_DTPG_VERSION,
    STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_INTL_DTPG
#ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
#endif
ZEND_GET_MODULE(intl_dtpg)
#endif



Сначала мы определяем хэндлеры, которые будут выполнятся в определённые фазы жизни нашего PHP объекта.

IntlDateTimePatternGenerator_object_dtor — деструктор PHP объекта. Тут нечего делать, вызываем стандартный API.
IntlDateTimePatternGenerator_object_free — освобождение памяти. Важная фаза, нужно сделать всё аккуратно, чтобы не было утечек памяти. Получаем нашу структуру, вызываем деструктор для zend_object, сбрасываем статус и уничтожаем объект С++ класса.
IntlDateTimePatternGenerator_object_create — выделение памяти под новый объект. Код подсмотрен в исходных кодах расширения intl.
Далее определяем методы нашего класса.
PHP_METHOD(IntlDateTimePatternGenerator, __construct) — констуктор, создаётся новый объект DateTimePatternGenerator.
Тут, в zend_parse_parameters, мы получаем строку в виде zend_string объекта, при этом в zend_parse_parameters указывается большая «S». Это нововведение в PHP 7. Но и старый способ с указанием маленькой «s» и получением отдельно C-style строки и её длины тоже работает, как мы видели в предыдущем расширении.
PHP_METHOD(IntlDateTimePatternGenerator, findBestPattern) — тут вызывается метод класса из ICU библиотеки.
Далее следует набор макросов для определения параметров методов класса. По имени параметров этих макросов в файле zend_API.h можно понять, что они значат.
В структуре zend_function_entry указываются методы класса. В макрос PHP_ME передаются ранее определённые наборы параметров и флаги. Эта структура используется при регистрации класса ниже.
PHP_MINIT_FUNCTION(intl_dtpg) — вызывается в момент инициализации расширения. Тут регистрируется наш класс и указываются хендлеры для обслуживания его фаз жизни.
PHP_MSHUTDOWN_FUNCTION(intl_dtpg) — когда нечего делать в момент shutdown, можно просто вернуть SUCCESS, а можно и не указывать и ниже, в zend_module_entry, указать NULL.
PHP_MINFO_FUNCTION(intl_dtpg) — добавляет информацию о расширении в вывод phpinfo()
Снова zend_function_entry, но в этот раз пустая — мы не определяем никаких функций в этом расширении.
zend_module_entry — тут мы указываем методы для инициализации и shutdown нашего расширения, другие два NULL это про request, мы ничего не делаем в момент инициализации и shutdown запроса.

tests


Небольшой тест, убеждаемся что паттерны генерируются:

001.phpt
--TEST--
Check for intl_dtpg presence
--SKIPIF--

--FILE--

--EXPECT--
d MMMM;MMMM d



Сборка и тестирование



phpize && ./configure && make
make test


valgrind — проверяем расширения на утечки памяти


Проверим наше расширение intl_dtpg на утечки памяти. Для этого создадим в папке расширения тестовый ini файл:

test-php.ini
extension=modules/intl_dtpg.so


и тестовый php файл:
test.php

https://habrahabr.ru/post/335478/


Метки:  

[Перевод] Практический бизнес онтологии: рассказ c передовой

Пятница, 11 Августа 2017 г. 23:34 + в цитатник


Перевод поста Стивена Вольфрама (Stephen Wolfram) "The Practical Business of Ontology: A Tale from the Front Lines".

Философия химических веществ


«Мы только должны решить: химикат — ближе к городу или к числу?» Я провел вчера свой день — как и большинство дней последних 30 лет — разрабатывая новые функции языка Wolfram. И вчера днем на одном из моих собраний была динамичная дискуссия о том, как расширить возможности языка в химии.

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

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

Мы построили целую систему на языке Wolfram для обработки того, что мы называем объектами . Объект может быть городом (например, Нью-Йорк), или фильмом, или планетой, или зиллионом других вещей. У объекта есть какое-то имя («Нью-Йорк»). И он имеет определенные свойства (например, население, площадь, дата основания, ...).

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

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

Но что мы пытались выяснить вчера, это то, как обращаться с абстрактными химическими веществами — химикатами, которые мы строим абстрактно, скажем, путем абстрактного графика, представляющего их химические структуры. Должны ли они быть представлены объектами, такими как вода или Нью-Йорк? Или их следует считать более абстрактными, например, списками чисел или, если на то пошло, математическими графами?

Ну, конечно, среди абстрактных химических веществ, которые мы можем построить, есть химические вещества, которые мы уже представляем объектами, такими как сахароза или аспирин. Но здесь есть важное отличие. Мы говорим об отдельных молекулах сахарозы или аспирина? Или как о продукте в массе?

На каком-то уровне это путаное различие. Потому что мы можем думать, что зная молекулярную структуру, мы знаем все — это просто вопрос расчета. И некоторые свойства — такие как молярная масса — в основном тривиальны для расчета по молекулярной структуре. Но другие – например, температура плавления — очень далеки от тривиальных.

Хорошо, но является ли это временной проблемой, на которой нельзя основывать долгосрочный дизайн языка? Или это нечто более фундаментальное, что никогда не изменится? Удобно то, что я достаточно занимаюсь фундаментальной наукой чтобы знать ответ: да, это нечто фундаментальное. Это связано с тем, что я называю вычислительной неприводимостью. К примеру, точное значение температуры плавления для бесконечного количества некоторого материала может фактически быть фундаментально невычислимым. (Это связано с неразрешимостью задачи замощения; установка плитки похожа на то, как молекулы составляют твердое тело).

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

По крайней мере, атомы должны быть в порядке


Хорошо, давайте поговорим об отдельных молекулах. Разумеется, они сделаны из атомов. И, по крайней мере, когда мы говорим об атомах, мы находимся на довольно прочной основе. Было бы логично сказать, что любая конкретная молекула всегда имеет в себе определенный комплект атомов — хотя, возможно, мы захотим рассмотреть «параметризованные молекулы», когда будем говорить о полимерах и т. п.

Но, по крайней мере, кажется безопасным рассматривать типы атомов как объекты. Как-никак, каждый тип атома соответствует химическому элементу, и на периодической таблице их имеется только ограниченное число. Конечно, в принципе можно придумать себе дополнительные «химические элементы»; и даже думать о нейтронной звезде как о гигантском атомном ядре. Но опять же, есть отличительная особенность: почти наверняка существует только ограниченное число фундаментально стабильных типов атомов, а большинство других имеют ужасно короткое время жизни.

Однако стоит сразу отметить. «Химический элемент — не на столько определен, как можно себе представить. Потому что это всегда смесь разных изотопов. И, скажем, от одной вольфрамовой шахты к другой, эта смесь может измениться, давая другую эффективную атомную массу.

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

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

Чтобы указать химический элемент, вам просто нужно указать его атомный номер Z. А затем учебники подскажут вам, что для указания конкретного изотопа вам просто нужно указать сколько нейтронов он содержит. Но это игнорирует неожиданный случай тантала. Потому что одна из естественных форм тантала (180mTa) на самом деле представляет собой возбужденное состояние ядра тантала, которое является очень стабильным. И чтобы правильно это определить, вы должны указать его уровень возбуждения, а также количество нейтронов.

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

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

Мы перешли от молекул к атомам к ядрам, так почему бы не говорить и об элементарных частицах? Что ж, это усложняет ситуацию. Да, есть известные частицы, такие как электроны и протоны — о которых довольно легко говорить — и они легко представлены объектами на языке Wolfram. Но есть и множество других частиц. Некоторые из них — такие, как ядра — довольно легко охарактеризовать. Вы можете сказать такие вещи, как: «Это особое возбужденное состояние системы c-кварк-анти-c-кварк» или что-то в этом роде. Но в физике частиц речь идет о квантовой теории поля, а не только о квантовой механике. И нельзя просто «подсчитать элементарные частицы»; также нужно иметь дело с возможностью виртуальных частиц и т. д. И в конечном итоге вопрос о том, какие частицы могут существовать — очень сложный, полный вычислительной неприводимостью. (Например, какие стабильные состояния могут быть в глюонном поле, это гораздо более сложный вопрос, похожий на задачу замощения, о которой я упоминал в связи с точками плавления.)

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

И в создании языка для описания мира нам нужно говорить в терминах вещей, которые действительно можно наблюдать и вычислять. Мы должны обратить внимание на основы физики — и не в последнюю очередь, для избегания тех позиций, которые могут в итоге ввести нас в замешательство. Нам также нужно обратить внимание на фактическую историю науки и фактические вещи, которые были измерены. Да, есть, например, бесконечное количество возможных изотопов. Но для множества целей очень полезно просто настроить объекты для тех, которые известны.

Пространство возможных химических веществ


Но так же ли обстоят дела в химии? В ядерной физике мы думаем, что знаем все достаточно стабильные существующие изотопы, поэтому любые дополнительные и экзотические из них будут очень кратковременными и, следовательно, возможно и не важными в практических ядерных процессах. Но в химии совсем другая история. Есть десятки миллионов химических веществ, которые люди изучают (и, например, помещали в научные публикации или патенты). И действительно, нет ограничений на количество молекул, которые можно было бы рассмотреть — и это может быть довольно полезным.

Но, хорошо, так как мы можем ссылаться на все эти потенциальные молекулы? Возможно, с первой аппроксимации мы можем указать их химические структуры, указав графики, в которых каждый узел является атомом, и каждое ребро является связью.

Что на самом деле означает «связь»? Хотя это невероятно полезно в практической химии, это на каком-то уровне неопределенное понятие — своего рода квазиклассическая аппроксимация полной квантовой механики. Существуют некоторые стандартные дополнительные аспекты: двойные связи, ионизационное состояние и т. д. Но на практике химический анализ очень успешно выполняется просто за счет характеризации молекулярных структур соответствующими метками графиков атомов и связей.

Хорошо, но должны ли химикаты быть представлены объектами или абстрактными графами? Если это химикат, о котором уже слышали, к примеру об углекислом газе, объект кажется удобным. Но что, если это новое химическое вещество, о котором никогда не говорили раньше? Можно подумать о том, чтобы изобрести новый объект для его представления.

Однако, любой уважающий себя объект будет иметь свое собственное имя. Какое было бы это имя? На языке Wolfram это может быть только граф, который представляет структуру. Но, возможно, хотелось бы чего-то похожего на обычное текстовое имя — на строку. У нас всегда есть способ IUPAC для названий химических веществ с именами, например 1,1'-{[3-(dimethylamino)propyl]imino}bis-2-propanol. Также есть более удобная для компьютера версия SMILES: CC(CN(CCCN©C)CC©O)O. И какой бы ни был граф, он всегда может генерировать одну из этих строк для ее представления.

Однако возникает новая проблема: строка не уникальна. Собственно, как бы кто-либо выбрал написать граф, он не может быть всегда уникальным. Конкретная химическая структура соответствует определенному графику. Но может быть много способов рисовать граф и множество разных представлений его. И на самом деле, даже проблема («изоморфизм графа») с определением того, соответствуют ли два изображения одному и тому же графу — трудно решаема.

Что такое химикат в конце?


Итак, давайте представим себе, что мы представляем химическую структуру графом. Сначала это абстрактная вещь. В графике есть атомы как узлы, но мы не знаем, как они будут расположены в реальной молекуле (и, например, на сколько ангстремов они будут отделены). Конечно, ответ не полностью определен. Говорим ли мы о низкоэнергетической конфигурации молекулы? (Что, если есть несколько конфигураций одной и той же энергии?) Предполагается ли то, что молекула должна быть сама по себе или в воде или в чем-то еще? Как должна была образоваться молекула? (Может быть, это белок, который сложен особым образом, когда он сошел с рибосомы.)

Что же, если бы у нас был объект, представляющий, скажем, «естественный гемоглобин», возможно, нам было бы лучше. Потому что в определенном смысле этот объект мог бы инкапсулировать все эти детали.

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

Но давайте поговорим о некоторых других случаях и аналогиях. Может быть, мы должны просто рассматривать все как объект. Как и любое целое число может быть объектом. Да, их бесконечное число. Но по крайней мере ясно, какие имена им следует давать. С действительными числами вещи уже в беспорядке. Например, уже нет такой уникальности, как с целыми числами: 0.99999… на самом деле то же самое, что и 1.00000 ..., но написано по-разному.

Как насчет последовательности целых чисел или, если на то пошло, математических формул? Все возможные последовательности или все возможные формулы, возможно, могут быть различными объектами. Но это не было бы особенно полезно, потому что многое из того, что хотелось бы сделать с последовательностями или формулами, — это войти в них и трансформировать их структуру. Но, что удобно для работы с объектами, это то, что каждый из них — это «единая сущность», в которую не нужно «заходить внутрь».

Так в чем же история с «абстрактными химическими веществами»? Это будет смешанность. Но, конечно, вы захотите «войти внутрь» и трансформировать данную структуру. Что говорит в пользу представления химического вещества графом.

Но тогда есть потенциально неприятный разрыв. У нас есть объект углекислого газа, о котором мы уже знаем много свойств. И потом у нас есть этот граф, который абстрактно представляет молекулу углекислого газа.

Мы можем опасаться, что это будет путать как людей, так и программы. Но первое, что нужно понять, это то, что мы можем отличить что представляют эти две вещи. Объект представляет собой натуральную версию химического вещества, свойства которого потенциально могут быть измерены. Граф представляет собой абстрактный теоретический химикат, свойства которого должны быть вычислены.

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

Философия встречает химию встречает математику встречает физику...


Во время описания некоторых из проблем, я понимаю, насколько сложно все это может показаться. И, да, это сложно. Но вчера на нашей встрече все прошло очень быстро. Конечно, помогает то, что все сталкивались с подобными проблемами раньше: это именно то, что лежит в основе того, чем мы занимаемся. Но каждый случай отличается.

И, каким-то образом, этот случай стал немного более глубоким и философским, чем обычно. «Давайте поговорим о наименовании звезд», — сказал кто-то. Очевидно, что есть близкие звезды, для которых у нас есть явные имена. И некоторые другие звезды, возможно, были идентифицированы в крупномасштабных исследованиях неба и даны определенные идентификаторы. Но в дальних галактиках много звезд, которые никогда не будут названы. Итак, как мы должны их представлять?

Это привело к разговорам о городах. Да, есть определенные города хартии, которым официально присвоены имена, — и мы, вероятно, имеем практически все эти имена на языке Wolfram, регулярно обновляемые. Но как насчет какой-то деревни, созданной для одного сезона некоторыми кочевыми людьми? Как мы должны это представлять? У нее есть определенное место, по крайней мере на некоторое время. Но разве это определенная вещь, или, может быть, она позже поделится на две деревни или вообще не в деревню?

Можно почти бесконечно спорить об идентификации — и даже о существовании — многих из этих вещей. Но в конечном итоге это не философия этих вещей, которые нам интересны: мы пытаемся создать программное обеспечение, которое люди найдут полезным. И поэтому в итоге важно то, что будет полезно.

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

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

Я рад сказать, что к концу нашего вчерашнего часового заседания (пополненного моим 40-летним опытом и 100-летним опытом всех присутствующих на собрании), мне кажется, мы выяснили основу действительно хорошего способа обращения с химическими веществами и химическими структурами. Должно пройти еще какое-то время, прежде чем он будет полностью разработан и реализован на языке Wolfram. Но идеи помогут понять, как мы вычисляем и рассуждаем о химии на многие годы вперед. И для меня, выяснение таких вещей — это чрезвычайно приятное времяпровождение. И я просто рад, что в моих длительных усилиях по развитию языка Wolfram я делаю многое.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335476/


Как попасть в ТОП: PR ДО релиза и в случае провала

Пятница, 11 Августа 2017 г. 23:15 + в цитатник
Сейчас сложно себе представить жизнь современного человека без смартфона, а смартфон уже совсем немыслим без мобильных приложений, число которых перевалило за полтора миллиона в App Store и Google Play. Поэтому часто пользователи даже и не подозревают, что уже разработано и опубликовано самое лучшее мобильное приложение.


— Так вы не читаете «Подиум».
— Нет.
— И никогда не слышали обо мне?
— Нет.


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


— Кто это?

Для продвижения в топ нужна комплексная рекламная кампания: таргетинговая реклама, продвижение в пабликах, мобильные баннерные сети, мотивированный трафик, немотивированные установки. Все это требует не только внушительных денежных затрат, но и недюжинной креативности, к примеру, Ваш пресс-релиз не опубликуют, если он неинтересно написан, в нем нет изображений или они плохого качества, не говоря уже о видео-контенте. Если хороший продукт снабдить качественным маркетингом, он абсолютно точно найдет своего потребителя. И это не всегда зависит от финансов. Вот несколько советов о том, как можно самостоятельно позаботиться о рекламе до запуска приложения, не потратив ни копейки, ни цента. А также несколько советов после старта продаж, если Вы всё же забыли о продвижении.


— Я еще только учусь разбираться во всех этих шмотках…
— «В этих шмотках»?


Ведите блог


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

Не увлекайтесь. Не стоит писать заметки длинной в несколько тысяч символов. Если соблазн написать литературное произведение слишком велик, используйте твиттер: 140 символов достаточно, чтоб напомнить о себе, но не настолько, чтоб отнять у Вас ценные часы рабочего времени. Посты должны быть регулярными, но не чаще 2 раз в неделю.

Соответствуйте


Если Вы делаете приложение для fashion-блогеров, снимите растянутый свитер. Создаете продукт для спортсменов? Сделайте хоть раз зарядку. По уму необходимо иметь представителя, который и будет соответствовать Вашему приложению, рекламное лицо, так сказать. Либо иметь отдельный рабочий аккаунт в социальных сетях.


— Так, теперь — Шанель. Необходима Шанель.

Преобразите свой профиль или создайте рабочий аккаунт:

– Аватарка/фото профиля


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

– Статус/шапка в твиттере/описание в инстаграме/обложка в фейсбуке


Это такой же рекламный канал. Оставьте там ссылке на Ваше приложение – QR-код или укажите ссылки на группы в соц.сетях. Креативное оформление здесь приветствуется.

– Подпишитесь на соответствующие группы;


И отпишитесь от всякой ерунды. «Громко посмеяться» можно и с личного аккаунта. А вот если Вам нравятся речитативное исполнение стихов под ритмическую музыку отечественных исполнителей, то лучше и аудио закрыть. Паблики, на которые Вы подписаны, указывают на круг Ваших интересов. Это работает и в обратную сторону: люди могут найти Вас среди участников групп. Избегайте публичных страниц политической и религиозной направленности.

– Контактные данные


Оставьте на странице актуальные контактные данные – номер телефона или почту. Что касается последней – осторожнее: почтовый адрес должен иметь представительный вид, например, содержать Ваши имя и фамилию. По возможности лучше обзавестись почтой на своем домене.


— Не подскажете, как пишется «Габбана»? Алло?

– Настройки приватности


Избавьте Ваших друзей и подписчиков от ненужной информации: отметьте в настройках страницы, какие уведомления от Вас будут отображаться в ленте новостей. Лучше оставить раздел с фотографиями, темами, возможно с друзьями, а вот изменение личной информации или добавление новых аудио – это излишняя информация.

Вы и представить не можете насколько порой клиенты/потребители дотошны в отношении истории компании или состава команды. Хуже не будет, если Вы поработаете над своим корпоративным имиджем, а вот на лояльность потребителей это точно положительно повлияет.

Ориентируйтесь на ЦА


Нет тонкой грани между «мне нравится» и «действительно нужно\красиво». Это довольно толстая линия и называется она – профессионализм.

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

Общайтесь


Общайтесь онлайн. Это не значит спамить под каждым постов или вступать в распри в комментариях. Если Вы прочитали/увидели что-то годное – похвалите автора. Есть вопросы? Задайте их в комментариях. Можете ответить на чей-то вопрос? Отвечайте.

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

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


— Конечно, двигайтесь как можно медленней. Вы знаете, как я это люблю.

Не забывайте о времени


Не срывайте сроков и дедлайнов. Не опаздывайте на конференции и встречи. Не выпадайте из социальных сетей, Ваши группы не должны пустовать. На деловых мероприятиях у ведущих всегда в запасе есть несколько шуток или интересных историй на случай неловких пауз, например, если спикер задерживается или что-то случилось с проектором. Ровно также у Вас должны быть «запасные» посты на случай творческого кризиса или отсутствия времени.

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


— Так, внимание. Боевая готовность!

Попробуйте следующие маркетинговые ходы:

– Истории неудач


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

– Блог


Напишите dev-story, раз не сделали это раньше. Наверняка у Вас сохранились видео или скриншоты, возможно интересные заметки процесса создания продукта. Поведайте начинающим разработчикам, как делать «не надо». Опубликуйте в своих официальных источниках (блог, сайт, группы в соцсетях) списки полезных веб-ресурсов, обязательных к посещению событий, попробуйте написать туториал или небольшие инструкции.

Заключение


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

Если Вы делаете небольшое приложение для узкого круга знакомых или для небольшой ЦА, следуйте нашим советам и у Вас обязательно все получится! Однако, если Вы разрабатываете серьезный проект или допустили какие-то ошибки в продвижении и провалили старт продаж, лучше обратитесь к профессионалам индустрии рекламы.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335474/


[recovery mode] IOTV — простой HTTP протокол для работы с сообщениями и командами IOT объектов в сервисе VIALATM

Пятница, 11 Августа 2017 г. 23:01 + в цитатник


Несколько разрработчиков, использующих сервис VIALATM для работы с IOT объектами, обратились ко мне с просьбой реализовать простой http протокол. Ранее они использовали MQTT протокол, но в силу каких-то причин им было необходимо более простое решение. Протокол реализован. В этой статье его краткое описание.


Для поддержки портокола IOTV в сервисе зарезервирован порт 7746 (для работы по протоклу https следеут использовать порт — 7745).

HTTP header


Все запросы по протоколу IOTV должны содержать в заголовке (http header) атрибут «iotv-user». Если в настройках учетной записи «IOTV password» установлен, атрибут «iotv-password» в заголвке должен совпадать с этим значением, в противном случае он может быть опущен.



Обязательный атрибут


Все запросы должны содеражать обязательный атрибут«root».



Дополнительные атрибуты


К запросам может быть добавлен атрибут «time». Он должен быть установлен в формате UNIX STAMP (количество секунд с 1 января 1970 года). Если этот атрибут опущен, то временем события считается время поступления запроса на сервер. Все прочие возможные атрибуты зависят от того, как определен объект IOT в сервисе. В ответ на запрос возвращаются текущие значения всех атрибутов объекта

Примеры


GET
Request: vialatm.com:7746/?root=HOME&A1=5&B1=12
Response: A1=5&B1=12&C1=14

POST
Request: vialatm.com:7746/

JSON
Data: {«A1»:«12»,«root»:«HOME»,«B1»:«44»}
Response: {«A1»:«12»,«B1»:«44»,«C2»:«12»}
XML
Data: 73HOME87
Response: 738788
POST FORM
Data: A1=543&root=HOME&B1=12&
Response: command=12.4&A1=543&B1=12&C1=14

Команды в ответ на запросы


Для IOT объектов можно определить команды



В этом случае, когда задается команда для объекта, она посылается в ответе на IOTV запрос:



Примеры ответов на запросы в этих случаях:

GET
Request: vialatm.com:7746/?root=HOME&A1=5&B1=12
Response: command=12.4&A1=5&B1=12&C1=14

POST
Request: vialatm.com:7746/

JSON
Data: {«A1»:«12»,«root»:«HOME»,«B1»:«44»}
Response: {«command»:«12.4»,«A1»:«12»,«B1»:«44»,«C2»:«12»}
XML
Data: 73HOME87
Response: 12.4738788
POST FORM
Data: A1=543&root=HOME&B1=12&
Response: command=12.4&A1=543&B1=12&C1=14
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335468/


Метки:  

Поиск сообщений в rss_rss_hh_full
Страницы: 1824 ... 1475 1474 [1473] 1472 1471 ..
.. 1 Календарь