निःशुल्क उपलब्धियां प्रणाली कोड के लिए सबसे अच्छा तरीका है


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जो मूल रूप से यह देखने के लिए जांचता है कि क्या उपयोगकर्ता को पहले से ही उस बैज से सम्मानित किया गया है, यदि नहीं, तो बैजबीबी तालिका को अपडेट करेगा। बैज क्लास एक उपयोगकर्ता के लिए सभी बैज को पुनः प्राप्त करने और इसे एक सरणी में वापस करने आदि का ख्याल रखता है, आदि (ताकि बैज को उपयोगकर्ता प्रोफ़ाइल पर प्रदर्शित किया जा सके)

क्या होगा जब सिस्टम पहले से ही पहले से ही लाइव साइट पर लागू हो?

एक "क्रोन" नौकरी क्वेरी भी है जिसे प्रत्येक बैज में जोड़ा जा सकता है। इसका कारण यह है क्योंकि जब बैज सिस्टम बहुत पहले लागू किया जाता है और initilaised होता है, तो पहले से अर्जित किए जाने वाले बैज को अभी तक सम्मानित नहीं किया जाना चाहिए क्योंकि यह एक इवेंट आधारित सिस्टम है। इसलिए प्रत्येक बैज की मांग पर एक 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);
  }
 }

}

जैसा कि उपरोक्त क्रोन वर्ग मुख्य बैज क्लास का विस्तार करता है, यह तर्क फ़ंक्शन का फिर से उपयोग कर सकता है try_award

इसका कारण यह है कि मैं इसके लिए एक विशेष क्वेरी बनाता हूं, हालांकि हम पिछली घटनाओं को "अनुकरण" कर सकते हैं, अर्थात प्रत्येक उपयोगकर्ता पोस्ट के माध्यम से जा सकते हैं और घटना वर्ग को ट्रिगर कर सकते हैं जैसे $event->trigger()यह बहुत धीमा होगा, विशेष रूप से कई बैज के लिए। इसलिए हम इसके बजाय एक अनुकूलित क्वेरी बनाते हैं।

उपयोगकर्ता को क्या पुरस्कार मिलता है ईवेंट के आधार पर अन्य उपयोगकर्ताओं को पुरस्कृत करने के बारे में

Badgeवर्ग awardसमारोह पर काम करता है user_id- वे हमेशा पुरस्कार दिया जाएगा। डिफ़ॉल्ट रूप से बैज उस व्यक्ति को प्रदान किया जाता है जिसने इस घटना को घटित किया है अर्थात सत्र उपयोगकर्ता आईडी (यह default_eventफ़ंक्शन के लिए सही है , हालांकि CRON जॉब स्पष्ट रूप से सभी उपयोगकर्ताओं और पुरस्कारों को अलग करता है)

तो चलो एक उदाहरण लेते हैं, एक कोडिंग चुनौती वेबसाइट पर उपयोगकर्ता अपनी कोडिंग प्रविष्टि प्रस्तुत करते हैं। व्यवस्थापक तब प्रविष्टियों का न्याय करता है और जब पूरा हो जाता है, तो सभी को देखने के लिए चुनौती पृष्ठ पर परिणाम पोस्ट करता है। जब ऐसा होता है, तो एक POSTED_RESULTS ईवेंट कहलाता है।

यदि आप पोस्ट की गई सभी प्रविष्टियों के लिए उपयोगकर्ताओं के लिए बैज प्रदान करना चाहते हैं, तो वे कहते हैं, यदि उन्हें शीर्ष 5 में स्थान दिया गया है, तो आपको क्रॉन जॉब का उपयोग करना चाहिए (हालांकि यह ध्यान में रखते हुए यह सभी उपयोगकर्ताओं के लिए अपडेट होगा, न कि केवल उस चुनौती के लिए परिणाम के लिए पोस्ट किया गया)

यदि आप क्रोन जॉब के साथ अपडेट करने के लिए अधिक विशिष्ट क्षेत्र को लक्षित करना चाहते हैं, तो आइए देखें कि क्या क्रोन जॉब ऑब्जेक्ट में फ़िल्टरिंग मापदंडों को जोड़ने का एक तरीका है, और उनका उपयोग करने के लिए 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);
   }
}

यदि पैरामीटर की आपूर्ति नहीं की जाती है, तब भी क्रॉन फ़ंक्शन काम करेगा।


संबंधित (शायद डुप्लिकेट): stackoverflow.com/questions/1744747/achievements-badges-system
गॉर्डन

2
यह संबंधित है लेकिन डुप्लिकेट नहीं है। कृपया दूसरा पैराग्राफ पढ़ें। "इस वेबसाइट पर बैज / अचीवमेंट सिस्टम के बारे में मेरी बहुत सारी समस्याएँ हैं, बस यह है - यह सब बात है और कोई कोड नहीं है। वास्तविक कोड कार्यान्वयन उदाहरण कहां है?"
गैरी ग्रीन

1
ठीक है, वर्किंग कोड लिखना एक निश्चित सीमा तक ही संभव है। मैं कहता हूं कि लोगों के लिए यह सामान्य है कि आप केवल सिद्धांत दें, एक बार कोई भी कार्यान्वयन बहुत जटिल होगा।
गॉर्डन

जवाबों:


9

मैंने एक बार इनाम प्रणाली लागू की है जिसमें आप एक दस्तावेज़ उन्मुख डेटाबेस कहेंगे (यह खिलाड़ियों के लिए एक कीचड़ था)। मेरे कार्यान्वयन से कुछ हाइलाइट्स, जिसका अनुवाद PHP और MySQL में किया गया है:

  • बैज के बारे में प्रत्येक विवरण उपयोगकर्ताओं के डेटा में संग्रहीत होता है। यदि आप MySQL का उपयोग करते हैं, तो मैंने सुनिश्चित किया है कि यह डेटा प्रदर्शन के लिए डेटाबेस में प्रति उपयोगकर्ता एक रिकॉर्ड में है।

  • हर बार जब कोई व्यक्ति कुछ करता है, तो कोड दिए गए ध्वज के साथ बिल्ला कोड को ट्रिगर करता है, उदाहरण के लिए ध्वज ('POST_MESSAGE')।

  • एक घटना भी काउंटर ट्रिगर कर सकती है, उदाहरण के लिए पदों की संख्या। increase_count ( 'POST_MESSAGE')। यहां आप एक चेक (या तो हुक द्वारा, या सिर्फ इस विधि में एक परीक्षण कर सकते हैं) कि यदि POST_MESSAGE की गिनती> 300 है, तो आपको बैज का इनाम देना चाहिए, उदाहरण के लिए: झंडा ("300_POST")।

  • ध्वज विधि में, मैं बैज को पुरस्कृत करने के लिए कोड डालूंगा। उदाहरण के लिए, यदि ध्वज 300_POST भेजा जाता है, तो बैज रिवॉर्ड_बैज ("300_POST") कहा जाना चाहिए।

  • ध्वज विधि में, आपके पास उपयोगकर्ताओं के पिछले ध्वज मौजूद होने चाहिए। इसलिए आप यह कह सकते हैं कि जब उपयोगकर्ता के पास FIRST_COMMENT, FIRST_POST, FIRST_READ आप बिल्ला ("NEW USER") देते हैं, और जब आपको 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_encode और json_decode का उपयोग करें।

  • कुछ अन्य उपयोगकर्ताओं द्वारा हेरफेर किए गए कुछ उपयोगकर्ताओं के डेटा पर ट्रैकिंग गतिविधि के लिए, आइटम पर एक डेटा संरचना जोड़ें और साथ ही काउंटर का उपयोग करें। मसलन रीड काउंट। बैज प्रदान करने के लिए ऊपर वर्णित उसी तकनीक का उपयोग करें, लेकिन अपडेट को निश्चित रूप से स्वयं के उपयोगकर्ता पोस्ट में जाना चाहिए। (उदाहरण के लिए लेख 1000 बार बिल्ला पढ़ें)।


1
बैज सिस्टम में क्लासिक प्रवृत्ति अपनी तालिका के लिए नए आंकड़े के लिए एक नया क्षेत्र जोड़ना है। मेरे लिए, यह एक आसान तरीका है और बुरे विचार का एक सा लगता है क्योंकि आपके स्टोर किए गए मिरर किए गए डेटा की गणना तालिका में पहले से मौजूद डेटा (शायद एक साधारण COUNT) से की जा सकती है, जो MyISAM तालिकाओं पर बहुत तेज़ है, 100% होगा सटीक)। यदि प्रदर्शन आपका लक्ष्य था, तो आपको अपडेट करने की आवश्यकता होगी और यदि बैज से सम्मानित किया जाना चाहिए, तो यह जांचने के लिए वर्तमान उदा post_count मान प्राप्त करें। आपको केवल एक क्वेरी, COUNT (*) की आवश्यकता हो सकती है। मैं अधिक जटिल आंकड़ों के लिए सहमत हूं, हालांकि एक क्षेत्र जोड़ने का अच्छा कारण होगा
गैरी ग्रीन

5
@ ग्रीन ग्रीन यह न केवल एक आसान तरीका है, यह स्केलेबल तरीका है और दस्तावेज़ डेटाबेस के साथ संगत है। शुद्धता के लिए, आप सही हैं, हालांकि एक बैज सिस्टम के लिए मैं इसे तुरंत और सबसे सटीक रूप से 100% सही और धीमा होने की संभावना है। एक सिंगल काउंट संभवत: त्वरित है, लेकिन जब आपके सिस्टम को तराजू और आपके पास बहुत सारे उपयोगकर्ता हैं, तो यह नहीं दिया जाता है कि रणनीति धारण की गई है।
नूबू

1
मुझे बैज डेफिनेशन टेबल, और बैज और उनकी वर्तमान प्रगति से उपयोगकर्ताओं को जोड़ने के लिए एक लिंक टेबल पसंद है। यह करने के लिए noSQL आपको उस समय जो कुछ भी स्कीमा में बंद कर देता है और बनाए रखने योग्य नहीं होता है जब अचानक बैज में टाइपो पाए जाते हैं, या 1000 नए बैज जुड़ जाते हैं। आपके पास त्वरित पुनर्प्राप्ति के लिए हमेशा दस्तावेज़ प्रक्रिया स्टोर में एक बैच प्रक्रिया कैश हो सकती है, लेकिन मैं चीजों को लिंक करना छोड़ दूंगा।
फ्लेवर्सस्केप

2

UserInfuser एक ओपन सोर्स गेमिफिकेशन प्लेटफॉर्म है जो बैजिंग / पॉइंट्स सर्विस को लागू करता है। आप यहां अपना एपीआई देख सकते हैं: http://code.google.com/p/userinfuser/wiki/API_Documentation

मैंने इसे लागू किया और कार्यों की संख्या न्यूनतम रखने का प्रयास किया। यहाँ एक 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);
}

अंतिम परिणाम विजेट के उपयोग के माध्यम से डेटा को सार्थक तरीके से दिखाना है। इन विगेट्स में शामिल हैं: ट्रॉफी केस, लीडरबोर्ड, मील के पत्थर, लाइव सूचनाएं, रैंक और अंक।

एपीआई का कार्यान्वयन यहां पाया जा सकता है: http://code.google.com/p/userinfuser/source/browse/trunk/serverside/api/api.py


1
क्या यह PHP आधारित है? यह सवाल PHP
लेनिन राज राजसेकरन

1
इसमें PHP बाइंडिंग है, लेकिन सर्वर साइड कोड पायथन में लिखा गया है।
नवराज चैहान

0

उपलब्धियां बोझ हो सकती हैं और इससे भी ज्यादा अगर आपको बाद में उन्हें जोड़ना है, जब तक कि आपके पास एक अच्छी तरह से गठित Eventवर्ग नहीं है।

यह उपलब्धियों को लागू करने की मेरी तकनीक में है।

मैं उन्हें पहले 'श्रेणियों' में विभाजित करना पसंद करता हूं और जिनके भीतर उपलब्धि है। यानी किसी killsखेल की एक श्रेणी में पहली हत्या के लिए 1 पुरस्कार, 10 दस हत्याएं, 1000 हजार हत्याएं आदि हो सकती हैं।

फिर किसी भी अच्छे अनुप्रयोग की रीढ़, अपनी घटनाओं को संभालने वाला वर्ग। फिर से एक खेल को मारने की कल्पना करता है; जब कोई खिलाड़ी कुछ मारता है, तो सामान होता है। हत्या को नोट किया गया है, आदि और वह एक केंद्रीकृत स्थान में सबसे अच्छी तरह से संभाला जाता है, जैसे और Eventsवर्ग जो अन्य स्थानों पर जानकारी भेज सकते हैं।

यह वहां पूरी तरह से गिर जाता है, कि उचित विधि में, अपनी Achievementsकक्षा को तत्काल करें और जांचें कि खिलाड़ी एक कारण है।

Achievementsवर्ग के निर्माण के रूप में यह तुच्छ है, बस कुछ ऐसा है जो डेटाबेस को यह देखने के लिए जांचता है कि खिलाड़ी के पास अगली उपलब्धि के लिए जितने आवश्यक हैं, उतने हैं।

मुझे Redis का उपयोग करके बिटफ़िल्ड में उपयोगकर्ता की उपलब्धियों को संग्रहीत करना पसंद है लेकिन उसी तकनीक का उपयोग MySQL में किया जा सकता है। यही है, आप खिलाड़ी की उपलब्धियों को एक के रूप में संग्रहीत कर सकते हैं intऔर फिर andउस इंट के साथ उस उपलब्धि को परिभाषित कर सकते हैं जो यह देखने के लिए है कि क्या उन्होंने इसे प्राप्त किया है। इस तरह यह intडेटाबेस में केवल एक कॉलम का उपयोग करता है।

इसका नकारात्मक पक्ष यह है कि आपको उन्हें अच्छी तरह से व्यवस्थित करना होगा और आपको अपने कोड में कुछ टिप्पणियां करने की आवश्यकता होगी ताकि आपको याद रहे कि 2 ^ 14 बाद में क्या होगा। यदि आपकी उपलब्धियों को उनकी अपनी तालिका में सम्‍मिलित किया गया है तो आप बस 2 ^ pk कर सकते हैं जहां pkउपलब्धियों की तालिका की प्राथमिक कुंजी है। जो चेक को कुछ इस तरह बनाता है

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

इस तरह आप बाद में उपलब्धियों को जोड़ सकते हैं और यह ठीक होगा, बस पहले से ही दी गई उपलब्धियों की प्राथमिक कुंजी को बदल दें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.