logo NodeSeekbeta

如何发起一场透明公开且科学合理的抽奖/T楼活动呢,有3大要素

前段时间办了一场论坛内的小型抽奖活动,让我再次想到了一直琢磨的一个问题:怎样的抽奖才是完善的抽奖呢,能够保证举办方和参与者都认同,降低暗箱操作的可能性呢。

先上结论,个人认为一个好的抽奖活动应该具备以下几点核心要素:

  • 结果不可提前预知,也就是主办方和用户都无从知晓
  • 结果可以复现,公布的抽奖结果必须能够按照既定算法复现
  • 教育成本和操作流程不能太过复杂,最好能够自动化或者通过点击实现

几种典型的反面教材:

  • 到了开奖时间或者开奖前,提前用某个骰子生成器抽几个出来,违背了结果不可复现
  • 用压缩包加密压缩提前生成的楼层,先公布压缩包,开奖后公布密码,违背了结果不可提前预知(主办方知道中奖楼层)
  • 用各种加密与秘钥交换算法生成一套算法出来,网站方与发帖人同时持有必须的秘钥之一,违背了成本/流程原则

其实在举办此活动之前,我一直想在论坛上直接加入抽奖功能,流程上大概是主题帖可以选择性附加一个抽奖活动,设定奖池大小/奖品数目/开奖时间等,然后论坛帮忙完成后续的自动化流程即可。但是思来想去总是没有好的方案出来,期间也搜索了一些现有的方案逛了逛知乎和v2ex寻找灵感,终究是不能得偿所愿。最后能想到的办法要么教育用户其原理所需的成本太高(原理复杂讲不清),要么流程过于复杂。

逛LES论坛发现了一个很不错的抽奖方法,稍加改造就用到了本次活动中,讲一下原理背景及操作流程:

熵联盟

由Cloudflare、洛桑联邦理工学院(EPFL)、智利大学、Kudelski Security等众多权威机构主导的一个去中心化的随机信标(random beacon),根据一系列复杂的技术原理保证随机的可靠性,对原理感兴趣的可以点击这个扩展阅读。这个随机信标每30s生成一个随机字符串,大概长这样:

afa0dd948208c2f1d0ee62c3f121d60d6e702576544815118f1639d165cfc593

你可以访问https://api.drand.sh/来获取任意周期(每30s一个周期)的随机信标结果,具体的开发者手册见这里。我们知道在随机取样过程中,虽然取样算法各有不同,但是只要保证所用伪随机数的种子一样,那么取样结果就是固定的。

image.png

诶嘿,你发现了没有,这个随机信标就很适合当随机种子呀。剩下的事情就简单了,我们只需要获取开奖时刻所在周期的随机信标作为随机种子,然后用一个公开的抽样算法来取样就可以了。Cloudflare Beacon还有个优点是可以查询任何历史周期的随机信标结果,保证了你抽奖之后任何时间回头检查都可以知道有没有作弊。
image.png

随机取样过程

取样算法的设计就比较多了,一般都是自己固定一套流程出来,保证可以复现就行了。为了偷懒我用了random.org的随机取样器,他也是支持用户输入随机种子的:

image.png

这里注意,整个取样过程实际上是个全排列过程,比方说到截止时间共有233个楼层,则生成1-233楼层的一个全排列,主办方根据自己的规定依次校核结果序列中每个楼层是否符合要求,比如去掉重复发帖和格式不符等等,这样就可以得到最终的中奖人员。
image.png

简单的自动化js脚本

讲了这么多其实就是一句话,用CF Beacon做随机种子,然后对楼层做全排列依次校核中奖人员即可。
那么有没有简化流程的全自动js脚本呢,应该是比较好写的,这里举个例子,读者可以根据自己的要求改动:

let openTime = 'Fri Dec 02 2022 12:00:00 GMT+0800 (China Standard Time)' // 开奖时间
let maxFloor = 233 // 最大楼层

let beaconRound = (new Date(openTime).getTime() / 1000 - 1595431050) / 30
const MAINCHAIN = '8990e7a9aaed2ffed73dbd7092123d6f289930540d7651336225dc172e51b2ce'
let beaconUrl = `https://api.drand.sh/${MAINCHAIN}/public/${beaconRound}`
fetch(beaconUrl)
    .then(r => r.json())
    .then(r => {
        let seed = r.randomness
        let randomOrgUrl = `https://www.random.org/sequences/?min=1&max=${maxFloor}&col=1&format=plain&rnd=id.${seed}`
        return fetch(randomOrgUrl)
            .then(r => r.text())
            .then(r => {
                console.log(`中奖楼层为${r.replace(/\n/g, ', ')}`)
            })
    })
123
  • 花里胡哨,净整些没用的

  • @仙风 #5

  • 沙发

  • @chenmo #1 来了啊~坐坐坐
    快记笔记学习一个!

  • 围观.支持楼主做成本论坛插件

  • @pony #3 哈哈哈好的,需求+1

  • 怎么抽奖不重要,重要的是我能不能抽中

  • 不管那么多有奖就行

  • 早就写过了,搞那么复杂不如自定义方便

    http://0755.tk/DrawLots.php

  • 虽然看不懂,但是很厉害的样子,都中奖~

123

你好啊,陌生人!

我的朋友,看起来你是新来的,如果想参与到讨论中,点击下面的按钮!

📈用户数目📈

目前论坛共有14978位seeker

🎉欢迎新用户🎉