Як в DB Best створили BI-проект статистики ігор в покер

У рубриці DOU Labs ми запрошуємо IT-компанії ділитися досвідом власних цікавих розробок і внутрішніх технологічних ініціатив. Питання і заявки на участь надсилайте на [email protected] .

Привіт, мене звати Олександр, я 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 дає переваги: вивчивши статистику ігор, можна легко передбачити поведінку суперників.

Опубліковано: 01/11/19 @ 11:00
Розділ Різне

Рекомендуємо:

5 книг, які навчать мислити системно, від Кирила Білобородько, Software Engineering Manager в EPAM
OS Daemonology: види, переваги, підводні камені
Криза перевиробництва джунов
AI & ML дайджест #15: вибір ML-фреймворку, вивчаємо TensorFlow 2.0 + Keras, шлях навчання Data Science
Як ми розробили Android-застосунок і втратили все, крім досвіду