Tired of outsourcing? Get hired at a top product startup from Silicon Valley 🚀
×Закрыть

Как в DB Best создали BI-проект статистики игр в покер

В рубрике DOU Labs мы приглашаем IT-компании делиться опытом собственных интересных разработок и внутренних технологических инициатив. Вопросы и заявки на участие присылайте на editors@dou.ua.

Привет, меня зовут Александр, и я BI-разработчик в компании DB Best. В нашей команде pet-проект родился сам собой, и сейчас он достаточно окреп, чтобы рассказать о нем. Мы создали с помощью Power BI-проект, который содержит статистику игр в покер за несколько лет, позволяет сформировать удобно организованный и красивый отчет о любой партии и любом игроке и на основании этих данных проанализировать и предсказать поведение соперников.

Идея

В 2017 году в DB Best организовался клуб игроков в покер. Совесть и руководство компании, естественно, не позволили бы участникам играть на деньги, поэтому прошедшие игры забывались, и скоро играть ради самого процесса стало не интересно. Так появилась идея вести статистику и отслеживать успехи игроков.

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

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

Реализация

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

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

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

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

Вот так выглядит пятерка самых успешных игроков этого года и их показатели:

А вот как с технической стороны работает «ачивка» нескольких побед подряд:

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

PreLatestGame = 
VAR vPlayer = Games[Player]
VAR vGameNumber = Games[Latest Game]
RETURN
MINX(
FILTER( Games,
Games[Player] = vPlayer
&& Games[Latest Game] > vGameNumber
),
Games[Latest Game]
) 

Prev Place = 
VAR vPlayer = Games[Player]
VAR vGameNumber = Games[PreLatestGame]
RETURN
MINX(
FILTER( Games,
Games[Player] = vPlayer
&& Games[Latest Game] = vGameNumber
),
Games[Place]

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

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

CMoney = 
IF (
COUNTROWS ( Games ) > 0,
CALCULATE (
[MMoney],
FILTER (
ALL ( 'Date'[Game Date] ),
'Date'[Game Date] <= MAX ( ( 'Date'[Game Date] ) )
)
),
BLANK ()

Репорт показывает статистику на любую дату или за любой период времени, так что можно бахвалиться своими победами, как Олимпиадой-1980.

Сложности

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

Изначально мы не предусмотрели, что, когда два человека одновременно выбивают кого-то из игры, приз должен делиться поровну. Аналогично, если по каким-то причинам игрок не доигрывает партию, а тот, кто играет за него, побеждает, все призы и победные очки должны делиться пополам. Для этих случаев мы придумали нетривиальное решение и применили DAX-формулы с созданием промежуточных агрегирующих in-memory-таблиц. Чтобы добавить этот функционал, нам пришлось изменить модель данных.

Для ведения дополнительных страниц статистики об успехах игрока в течение месяца, года или игрового сезона использовались calculated tables. Например, для определения лучших игроков месяца (по таким критериям, как доход, призовые места и посещение игр в целом):

Awards Monthly = SUMMARIZE(Games,'Date'[Game Month],Games[Player],"Games",COUNT(Games[GameDate]),"Pts",[Pts],"Money",[Money])

Поверх этой таблицы мы вычисляем ранг игрока в пределах месяца:

Money Rank = var GM = 'Awards Monthly'[Game Month]
return RANKX(FILTER(ALL('Awards Monthly'),'Awards Monthly'[Game Month]=GM),'Awards Monthly'[Money])

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

Или, например, можно определить беспрерывную серию игр.

В основной таблице определяется номер игры, с которой началась эта серия:

Game Sequence Started = 
VAR vPlayer = Games[Player]
VAR vGameNumber = Games[Latest Game]
RETURN
MINX(FILTER(Games,
Games[Player] = vPlayer
&& Games[Game Seq Reset] = 1
&& Games[Latest Game] >= vGameNumber)
,Games[Latest Game])

В вычисляемой таблице:

LatestPlayerGame = SUMMARIZE(Games,Games[Player],"Max Game Date",MAX(Games[GameDate]),"Latest Game",MIN(Games[Latest Game]))

Колонка с номером игры в беспрерывной серии:

Latest Serie Started = MINX(Games,CALCULATE(VALUES(Games[Game Sequence Started]),FILTER(Games,Games[Player]=LatestPlayerGame[Player] && Games[GameDate]=LatestPlayerGame[Max Game Date])))

Latest Serie = IF(LatestPlayerGame[Latest Game]<>1,BLANK(),[Latest Serie Started] - LatestPlayerGame[Latest Game] + 1)

Latest Serie — это и есть количество беспрерывно сыгранных турниров. Этот показатель также делится на максимально успешную для игрока за все время ведения статистики и «текущую» серию.

Например, игрок Ilya участвовал в 22 играх, не пропустив ни одного раза, и сыграл без перерывов в 6 последних игр.

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

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

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

Результат

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

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

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

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

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

LinkedIn

2 комментария

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

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

Подписаться на комментарии