走格子抽奖设计小谈

概要

走格子抽奖是游戏中常见的抽奖方式之一。早年大富翁丢骰子走格子的玩法已经深入人心,容易理解,充满变数和乐趣,在现在的游戏中依然有较广泛的应用。

为了方便讨论,我们先引入计算机图论中的一个概念:连通图。

A graph is connected when there is a path between every pair of vertices. In a connected graph, there are no unreachable vertices. A graph that is not connected is disconnected. A graph G is said to be disconnected if there exist two nodes in G such that no path in G has those nodes as endpoints.

From Wikipedia - Connected graph

简单的讲,若无向图G中任意一对顶点都是连通的,则此图是连通图。

以下是从网络中找到的一写连通图类抽奖玩法截图:


剑灵-魔皇棋盘

无向图,非全奖


蜀山飘渺录-夺宝阁

无向图,全奖


大富翁8-游戏地图

无向连通图,全奖

本文主要讨论两个问题:

  1. 走格子抽奖玩法的设计要素
  2. 走格子抽奖玩法的实例分析

设计要素

地图设计

地图是连通图类抽奖的基础和难点。在抽奖活动中通常需要考虑两个问题。

一是地图路线。长度有多长?是否有岔路?若有岔路岔路的分支逻辑如何制定?每次行动的距离在什么区间?每次行动,落在行动距离区间上各个点的概率是否相同?

二是地图点内容。每格的内容是什么?是奖品还是事件?如果是奖品,如何控制奖品价值的投放?如果是事件,会对玩家产生什么影响?

以上两点问题基本决定了整个抽奖系统的逻辑与进出模型,需要综合考虑。

玩法设计

解决了之前的地图设计,玩法设计相较而言就是比较容易解决的问题了。此处的玩法设计主要是针对基础掷骰子走格子玩法进行的改良和补充。

  1. 获取行动次数的方式:定价、购买次数限制、非付费获取方式
  2. 阶段性/保底奖励的设计:政策风险
  3. 与其他系统的联系:奖品内容和行动次数道具

实例分析

选取几个走格子抽奖设计过程中的典型问题进行分析:

有限长线性路径到达终点的成本

Q:

有一个长L格的线性路径,玩家行动IDX。格子上不存在会影响玩家行动的随机事件。点数和大于等于L均视为到达终点。

若L=10,I=1,X=6,求玩家到达终点需要投掷骰子次数N的期望。

A:

解决方法有很多,例如卷积公式、马科夫过程、递归、程序模拟等。

试举两解如下:

i. 递归

设点数和大于等于G时需要投骰子的次数为N,Px为投出各点数的概率,有:

N(G)=P6×N(G-6)+P5×N(G-5)+P4×N(G-4)+P3×N(G-3)+P2×N(G-2)+P1×N(G-1)+1

其中

N(0)=N(-1)=N(-2)=N(-3)=N(-4)=N(-5)=0

P1=P2=P3=P4=P5=P6=1/6

求解得到:

ii. 程序模拟

建立一个[1,6]的离散均匀分布来作为每次投骰子的结果集,然后写个简单的循环就成,求解得到:

误差可接受,故得到一个10格长线性路径到达终点需要的掷骰子次数为3.32次。

有限长线性路径落在各个格子上的概率

真实掷骰的情形

真实掷骰即每次掷骰子是公平的,即每次掷出1-6点的概率均为1/6

与之前计算终点到达成本相似,有多种解决方法,这里直接使用程序模拟,因为其实现非常简单:

# coding=utf-8
from __future__ import division
import random
import math
max_round = 1000000
current_round = 1
min_rd = 1
max_rd = 6
start_pos = 1
current_pos = 0
end_pos = 10
count_list = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
for current_round in range(1,max_round+1):
    current_pos = start_pos
    while current_pos < end_pos:
        current_pos += random.randint(min_rd, max_rd)
        if current_pos in range(1, end_pos+1):
            count_list[current_pos-1] += 1
print count_list

分别求路径长度为10,20下各个路径点的到达次数分布如下:

大致看来有两个特征:

  1. 2-7呈一个明显的局部区间,7全局最高,区间内概率配比相对稳定且与路径长度无关
  2. 路径后部的点概率分布趋于均衡,路径长度越长该现象越明显

由于特征2的存在,导致公平掷骰下的有限长路径走格子游戏中,路线后段的落中概率几乎趋同,除了通过控制单格投放奖品的数量和投放相同奖品的格数,我一时半会还想不到其他可以有效区分奖品概率的方式。

既然存在奖品难以配置的问题,那为什么还有很多游戏做同类型的抽奖玩法呢?解答这个问题前,我们可以先去浏览以下对此类玩法进行了概率公示的游戏,其奖品概率分布到底是什么样的。

观察到这是一个60格长的线性路径,其公布的奖品共有27种,原因是同一种奖品在多个格子进行了配置的缘故。

我们主要关注其概率分布。可以从很多个角度说明其采用的很有可能不是真实掷骰:

  • 减速之球位于44顺位,残废之球位于6顺位,然而概率均是0.20%
  • 投掷能量和铁铲,这两个奖品均分布在3格,投掷能量在前30格,铁铲在后30格,但概率和均为4.18%
  • 属性洗炼卡位于3顺位,承太郎皮肤位于4顺位,然而承太郎皮肤的概率是洗炼卡概率的2倍
  • 还有很多,不再一一列举

以上现象均与之前程序模拟的结果相悖。我们有一定的根据认定这款游戏采用的不是真实掷骰。事实上,如果研究其他游戏中的类似系统,不难发现其概率分布大都不符合真实掷骰子的情形,说明其采用的可能是其他方式,具体讨论见后续章节。

不公平掷骰的情形

为了控制奖品成本,即让物品抽中的概率与其价值成反比,我们可以人为的
干预每次掷骰子产出结果的概率。以下给出一种最简单的实现。

已知路径n格上的奖品各自价值vi,定义一个权重pi,大体满足pi∝(1/vi)即可。

若每次行动距离的区间为[1,l],本次行动起点为s,则本次行动得到行动距离k的概率为:

P(k)=p(s+k)/sigma(p(i),i= s to s+l),k∈[1,l]

即由行动距离范围内的奖品权值分布影响行动距离。

当然以上给出的只是一种简单实现,通过更复杂的设计,设计合理时可实现上一章节截图中的整体奖品概率可控。

连通图落在各个格子上的概率

连通图情形下的走格子游戏,就与真实的大富翁游戏非常接近了。真实游戏中使用这种模型的走格子抽奖玩法的情形可能比较少,因为完成这样一个玩法的工作量其实跟做一个类大富翁游戏差不了多少(笑)

为了方便讨论,先对模型做以下简化:

  1. 岔路上选择每一个相邻路径点的概率均相同
  2. 游戏路径上每个节点的度不小于2
  3. 无向连通图

无向连通图中,玩家每次行动的步数在地图足够大时其实已经不影响实际结果,且满足马科夫性质。

先从以上最简单的九宫格无向连通图开始分析。
路径点1作为起点,每次掷骰子,然后开始行动。大样本下其实起点的选取和每次掷骰子的点数都不影响结果。故每次行动都从当前节点的相邻节点中平均概率选择一个路径点作为行走目标,测试一下行走100000步后,玩家在各个路径点停留的次数,继续使用程序模拟:

local tbMap = {
    [1] = { 2, 4 },
    [2] = { 1, 3, 5 },
    [3] = { 2, 6 },
    [4] = { 1, 5, 7 },
    [5] = { 2, 4, 6, 8 },
    [6] = { 3, 5, 9 },
    [7] = { 4, 8 },
    [8] = { 5, 7, 9 },
    [9] = { 6, 8 },
}
function FindNextPos(nCurPos)
    local tbNext = tbMap[nCurPos]
    local tbPos = {}
    for k, v in pairs(tbNext) do
        table.insert(tbPos, v)
    end
    local nNext = tbPos[math.random(#tbPos)] or 0
    return nNext
end
function tst_roll_analyze(nRoll)
    local tbResult = {}
    local nCurPos = 5
    for i = 1, nRoll do
        nCurPos = FindNextPos(nCurPos)
        tbResult[nCurPos] = tbResult[nCurPos] or 0
        tbResult[nCurPos] = tbResult[nCurPos] + 1
    end
    for i = 1, #tbResult do
        print(tbResult[i])
    end
end
tst_roll_analyze(100000)

得到的结果如下

无向连通图情形下,得到的结果显然有规律的多。猜测如下:

  • 落在格子上的概率与该格子的度成正比例关系

感兴趣的同好可以继续在更复杂的无向连通图上测试该结论,其实仍然成立。但数学形式的证明目前还无法给出(毕竟我太菜了)

对应到具体走格子抽奖活动上,如果是无向连通图,那么只需要在度数高的节点上配置低价值奖品即可。

总结

走格子抽奖活动有多种实现方式,相较而言市面上目前使用比较多的是有限长度的线性路径。不同类型的走格子抽奖活动其奖品概率分布完全不同,需要认真体会其中原理和数值配置,根据实际情况选择适合的模型进行设计。

Title:走格子抽奖设计小谈

Author:Fancydz

Publish:2018.03.19 12:03

Update:2018.03.20 00:03

Path:https://fancydz.github.io/2018/03/19/2018031901/

License: Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0) This work is licensed under a Creative Commons Attribution 4.0 International License.