Найкращий спосіб кодування системи досягнень


85

Я думаю про найкращий спосіб розробити систему досягнень для використання на моєму сайті. Структуру бази даних можна знайти в найкращому способі повідомити про відсутність 3 або більше послідовних записів, і цей потік справді є розширенням для отримання ідей від розробників.

Проблема, з якою я багато розмовляю про значки / системи досягнень на цьому веб-сайті, полягає саме в цьому - це все розмови і відсутність коду. Де фактичні приклади реалізації коду?

Тут я пропоную дизайн, який, я сподіваюся, міг би зробити свій внесок і, сподіваюся, створити хороший дизайн для кодування розширюваних систем досягнень. Я не кажу, що це найкраще, далеко не так, але це можливий стартовий блок.

Будь ласка, не соромтеся внести свої ідеї.


моя ідея дизайну системи

Здається, загальним консенсусом є створення "системи, заснованої на подіях" - всякий раз, коли відома подія відбувається, як повідомлення створюється, видаляється тощо, він викликає клас подій так ...

$event->trigger('POST_CREATED', array('id' => 8));

Клас подій потім з’ясовує, які значки «прослуховують» для цієї події, потім цей requiresфайл і створює екземпляр цього класу приблизно так:

require '/badges/' . $file;
$badge = new $class;

Потім вона викликає подію за замовчуванням, передаючи дані, отримані при виклику trigger;

$badge->default_event($data);

значки

Тоді тут відбувається справжня магія. кожен знак має власний запит / логіку, щоб визначити, чи слід присвоювати значок. Кожен значок викладено, наприклад, у такому форматі:

class Badge_Name extends Badge
{
 const _BADGE_500 = 'POST_500';
 const _BADGE_300 = 'POST_300';
 const _BADGE_100 = 'POST_100';

 function get_user_post_count()
 {
  $escaped_user_id = mysql_real_escape_string($this->user_id);

  $r = mysql_query("SELECT COUNT(*) FROM posts
                    WHERE userid='$escaped_user_id'");
  if ($row = mysql_fetch_row($r))
  {
   return $row[0];
  }
  return 0;
 }

 function default_event($data)
 {
  $post_count = $this->get_user_post_count();
  $this->try_award($post_count);
 }

 function try_award($post_count)
 {
  if ($post_count > 500)
  {
   $this->award(self::_BADGE_500);
  }
  else if ($post_count > 300)
  {
   $this->award(self::_BADGE_300);
  }
  else if ($post_count > 100)
  {
   $this->award(self::_BADGE_100);
  }

 }
}

awardФункція походить від розширеного класу, Badgeякий в основному перевіряє, чи користувач вже отримав цей значок, якщо ні, то оновить таблицю значків db. Клас значків також бере на себе отримання всіх значків користувача та повернення їх у масиві тощо (тому значки можуть відображатися, наприклад, у профілі користувача)

як щодо того, коли система вперше впроваджується на вже діючому сайті?

Існує також запит на роботу "cron", який можна додати до кожного значка. Причиною цього є те, що коли система значків вперше впроваджується та ініціюється, значки, які вже мали бути зароблені, ще не присуджуються, оскільки це система, заснована на подіях. Тож робота CRON проводиться на вимогу кожного значка, щоб нагородити все, що потрібно. Наприклад, робота CRON для вищезазначеного буде виглядати так:

class Badge_Name_Cron extends Badge_Name
{

 function cron_job()
 {
  $r = mysql_query('SELECT COUNT(*) as post_count, user_id FROM posts');

  while ($obj = mysql_fetch_object($r))
  {
   $this->user_id = $obj->user_id; //make sure we're operating on the right user

   $this->try_award($obj->post_count);
  }
 }

}

Оскільки вищезазначений клас cron розширює основний клас значків, він може повторно використовувати логічну функцію try_award

Причина, по якій я створюю для цього спеціалізований запит, полягає в тому, що ми могли б "імітувати" попередні події, тобто переглядати кожну публікацію користувача та запускати клас подій, як би $event->trigger()це було дуже повільно, особливо для багатьох значків. Тому ми замість цього створюємо оптимізований запит.

який користувач отримує нагороду? все про нагородження інших користувачів на основі події

Функція Badgeкласу awardдіє user_id- вони завжди отримають нагороду. За замовчуванням значок присуджується особі, яка ВИКОНИЛА подію, тобто ідентифікатор користувача сеансу (це справедливо для default_eventфункції, хоча завдання CRON, очевидно, циклічно переглядає всіх користувачів та нагороджує окремих користувачів)

Отже, давайте візьмемо приклад, на веб-сайті, що викликає кодування, користувачі подають свої записи щодо кодування. Потім адміністратор розглядає записи, а після завершення публікує результати на сторінці виклику, щоб усі бачили. Коли це трапляється, викликається подія POSTED_RESULTS.

Якщо ви хочете нагородити значки для користувачів за всі опубліковані записи, скажімо, якщо вони потрапили в топ-5, вам слід використовувати завдання cron (хоча, маючи на увазі, це буде оновлено для всіх користувачів, а не лише для цього завдання) результати були опубліковані для)

Якщо ви хочете націлити на більш конкретну область для оновлення за допомогою завдання cron, давайте подивимось, чи є спосіб додати параметри фільтрації в об’єкт завдання cron, і отримати функцію cron_job для їх використання. Наприклад:

class Badge_Top5 extends Badge
{
   const _BADGE_NAME = 'top5';

   function try_award($position)
   {
     if ($position <= 5)
     {
       $this->award(self::_BADGE_NAME);
     }
   }
}

class Badge_Top5_Cron extends Badge_Top5
{
   function cron_job($challenge_id = 0)
   {
     $where = '';
     if ($challenge_id)
     {
       $escaped_challenge_id = mysql_real_escape_string($challenge_id);
       $where = "WHERE challenge_id = '$escaped_challenge_id'";
     }

     $r = mysql_query("SELECT position, user_id
                       FROM challenge_entries
                       $where");

    while ($obj = mysql_fetch_object($r))
   {
      $this->user_id = $obj->user_id; //award the correct user!
      $this->try_award($obj->position);
   }
}

Функція cron все одно працюватиме, навіть якщо параметр не вказаний.


Пов’язане (можливо, дублікат): stackoverflow.com/questions/1744747/achievements-badges-system
Гордон,

2
Це пов’язано, але не повторюється. Будь ласка, прочитайте другий абзац. "Проблема, з якою я багато розмовляю про значки / системи досягнень на цьому веб-сайті, полягає саме в цьому - це все обговорення і відсутність коду. Де фактичні приклади реалізації коду?"
Gary Green

1
ну, написання робочого коду можливо лише до певної міри. Я б сказав, що для людей досить нормально давати вам лише теорію, як тільки будь-яка реалізація буде занадто складною.
Гордон,

Відповіді:


9

Одного разу я запровадив систему винагород у так звану базу даних, орієнтовану на документи (це було грязю для гравців). Кілька основних моментів моєї реалізації, перекладених на PHP та MySQL:

  • Кожна інформація про значок зберігається в даних користувачів. Якщо ви використовуєте MySQL, я б переконався, що ці дані містяться в одному записі на кожного користувача в базі даних для підвищення продуктивності.

  • Кожного разу, коли людина, про яку йде мова, щось робить, код запускає код значка із заданим прапором, наприклад прапором ('POST_MESSAGE').

  • Одна подія може також викликати лічильник, наприклад, підрахунок кількості повідомлень. збільшення_рахування ('POST_MESSAGE'). Тут ви можете перевірити (або за допомогою гачка, або просто провівши тест за цим методом), що якщо кількість POST_MESSAGE> 300, ви повинні нагородити значок, наприклад: прапор ("300_POST").

  • У методі прапора я б поставив код для нагородження значками. Наприклад, якщо надіслано прапор 300_POST, тоді слід викликати значок_наз_значки ("300_POST").

  • У методі прапора ви також повинні мати присутні попередні прапори користувачів. щоб ви могли сказати, коли у користувача є FIRST_COMMENT, FIRST_POST, FIRST_READ, ви надаєте значок ("НОВИЙ КОРИСТУВАЧ"), а коли ви отримуєте 100_COMMENT, 100_POST, 300_READ, ви можете надати значок ("EXPERIENCED_USER")

  • Всі ці прапори та значки потрібно якось зберігати. Використовуйте якийсь спосіб, де ви вважаєте прапори бітами. Якщо ви хочете, щоб це зберігалося насправді ефективно, ви вважаєте їх бітами і використовуєте код нижче: (Або ви можете просто використати пустий рядок "000000001111000", якщо не хочете такої складності.

$achievments = 0;
$bits = sprintf("%032b", $achievements);

/* Set bit 10 */
$bits[10] = 1;

$achievements = bindec($bits);

print "Bits: $bits\n";
print "Achievements: $achievements\n";

/* Reload */

$bits = sprintf("%032b", $achievments);

/* Set bit 5 */
$bits[5] = 1;

$achievements = bindec($bits);

print "Bits: $bits\n";
print "Achievements: $achievements\n";
  • Приємним способом зберігання документа для користувача є використання json і зберігання даних користувачів в одному текстовому стовпці. Використовуйте json_encode та json_decode для зберігання / отримання даних.

  • Для відстеження активності деяких даних користувачів, якими маніпулює інший користувач, додайте структуру даних до елемента та використовуйте там також лічильники. Наприклад, кількість підрахунків. Використовуйте ту саму техніку, що описана вище для нагородження значками, але оновлення, звичайно, має надходити до власного повідомлення користувачів. (Наприклад, статтю прочитано 1000 разів).


1
Класичною тенденцією в системах значків є додавання нового поля для нової статистики до вашої таблиці. Для мене це здається трохи простим виходом і поганою ідеєю, тому що ваше зберігання дзеркальних даних, які можна обчислити за даними, що вже є в таблиці (можливо, простий COUNT (), який ДУЖЕ швидкий для таблиць MyISAM, буде 100% точні). Якщо вашою метою була продуктивність, вам потрібно буде оновити І вибрати, щоб отримати поточне, наприклад, значення post_count, щоб перевірити, чи слід присвоювати значок. Вам може знадобитися лише один запит, COUNT (*). Я погоджуюсь, що для більш складних даних була б вагома причина, щоб додати поле
Гері Грін

5
@Gary Green Це не лише простий вихід, це також масштабований спосіб і сумісний з базами даних документів. Що стосується правильності, то ви маєте рацію, хоча для системи значків я б волів, щоб вона була швидкою і швидше за все правильною, ніж 100% правильною і повільною. Один підрахунок, ймовірно, швидкий, але коли ваша система масштабується, і у вас багато користувачів, це не означає, що стратегія діє.
Кнубо

1
Мені подобається ідея просто мати таблицю визначення значків та таблицю посилань, щоб зв’язати користувачів зі значками та їх поточний прогрес. Виконуючи це, noSQL замикає вас на будь-якій схемі на той час і не підтримується, коли раптом знайдені помилки в значках або додано 1000 нових значків. Ви завжди можете кешувати пакетний процес у більшій частині сховища документів для швидкого пошуку, але я б залишав речі пов’язаними.
FlavorScape

2

UserInfuser - це платформа гейміфікації з відкритим кодом, яка реалізує службу бейджів / балів. Ви можете перевірити його API тут: http://code.google.com/p/userinfuser/wiki/API_Documentation

Я реалізував його і постарався зберегти мінімальну кількість функцій. Ось API для php-клієнта:

class UserInfuser($account, $api_key)
{
    public function get_user_data($user_id);
    public function update_user($user_id);
    public function award_badge($badge_id, $user_id);
    public function remove_badge($badge_id, $user_id);
    public function award_points($user_id, $points_awarded);
    public function award_badge_points($badge_id, $user_id, $points_awarded, $points_required);
    public function get_widget($user_id, $widget_type);
}

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

З реалізацією API можна ознайомитись тут: http://code.google.com/p/userinfuser/source/browse/trunk/serverside/api/api.py


1
це засновано на PHP? Питання засноване на PHP
Ленін Радж Раджасекаран

1
Він має прив'язки PHP, але код на стороні сервера написаний на Python.
Navraj Chohan

0

Досягнення можуть бути обтяжливими і тим більше, якщо вам доведеться додавати їх пізніше, якщо у вас немає добре сформованого Eventкласу.

Це вкладається в мою техніку реалізації досягнень.

Я люблю поділяти їх спочатку на "категорії", і всередині них є рівні досягнення. тобто killsкатегорія в грі може мати нагороду 1 за перше вбивство, 10 десять вбивств, 1000 тисяч вбивств тощо.

Тоді до хребта будь-якого хорошого додатка, класу, який обробляє ваші події. Знову уявляючи гру з вбивствами; коли гравець щось вбиває, трапляються речі. Вбивство зазначається і т. Д., І це найкраще обробляти в централізованому місці, наприклад і в Eventsкласі, який може надсилати інформацію в інші залучені місця.

Там цілком доречно, щоб за належним методом створити інстанцію вашого Achievementsкласу та перевірити, чи відповідає гравцеві.

Як побудова Achievementsкласу, це тривіально, просто те, що перевіряє базу даних, чи має гравець стільки вбивств, скільки потрібно для наступного досягнення.

Мені подобається зберігати досягнення користувача в BitField за допомогою Redis, але такий же прийом можна використовувати в MySQL. Тобто, ви можете зберігати досягнення гравця як, intа потім andцей int з бітом, який ви визначили як це досягнення, щоб побачити, чи вони його вже здобули. Таким чином він використовує лише один intстовпець у базі даних.

Недоліком цього є те, що ви повинні добре їх організувати, і вам, швидше за все, потрібно буде зробити кілька коментарів у своєму коді, щоб згодом ви запам’ятали, чому відповідає 2 ^ 14. Якщо ваші досягнення перераховані у власній таблиці, ви можете просто зробити 2 ^ pk, де pkє первинний ключ таблиці досягнень. Це робить чек приблизно таким

if(((2**$pk) & ($usersAchInt)) > 0){
  // fire off the giveAchievement() event 
} 

Таким чином, ви можете додати досягнення пізніше, і це буде прекрасно поєднуватися, просто НІКОЛИ не змінюйте основний ключ вже отриманих досягнень.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.