本文详细介绍了一种经验方法,以确定在Epic的Fortnite Battle Royale中降落的前10个位置。 此外,还显示了每个区域的最短掠夺路径。 最后,给出了用于生成此列表的代码的演练。
在开始列表之前,我应该介绍用于对降落点进行排名的度量。 可以将这一指标广义地描述为“可以在着陆时迅速获得的最有可能的战利品盒子”。 有关此指标的更多详细信息,请参见代码。
- Fortnite是下一个伟大的电子竞技吗?
- 哦,是的,它是LadiesNITE!
- 公告:WIVK和WNML赞助BBG诺克斯维尔活动
- 为什么我认为Fortnite对儿童不健康
- 看到什么坚持:Fortnite的史诗哲学
此外,鉴于我拥有的数据,我不得不做一些假设
- 玩家可以完美的直线运动。 在实际游戏中,显然存在阻碍玩家这样做的障碍
- 不考虑胸部抬高。 这将导致此分析非常偏向于在不同海拔高度上具有许多战利品箱的地点,例如倾斜塔
- 玩家开始准确地降落在第一个战利品盒上
- 使用一种算法来定义战利品盒的集群以及最短路径。 这意味着它可能会犯人通常不会犯的错误
- 可能没有所有战利品盒子。 如果发现任何丢失,请访问http://www.fortnitechests.info/并报告包装盒的位置! 对于我来说,该网站也是一个了不起的数据源,无论将其放在一起,我都非常感谢!
事不宜迟,让我们开始清单!
1.斜塔
我认为很多人不会觉得倾斜塔成为第一名特别令人惊讶。 在非常紧凑的区域中,如果有超过30个战利品箱,那么从理论上讲,在游戏开始的60秒内可以抢夺15个战利品箱。 在现实中这是不可行的,但按此指标仍然是表现最佳的。 最佳战利品路径如下所示。 绿色标记指示开始的战利品框,红色指示最后的战利品框:
战利品箱总数:33

2.工厂
列表上的第二个数字是在E9扇区中未命名的“工厂”区域。 尽管许多人都知道这个景点,但它的拥挤程度通常不如倾斜塔高。
战利品箱总数:14

3.零售行
零售行在列表中排名第三。 该区域经常很拥挤,这是有充分理由的。
战利品箱总数:17

4.鬼山
鬼山是我的个人最爱。 有很多战利品箱子,很少有人在那里。 通常,您会迷迷糊糊地长时间跋涉到第一个圈子,但是如果您可以有效地进行抢劫,这对您的影响不会很大。
战利品箱总数:16

5.宜人的公园
宜人的公园排在第5位。 我经常发现这个地方太拥挤了,以至于我不喜欢,但是如果您能解决这个问题,那么会有很多战利品!
战利品箱总数:16

6.诺比海岸
Snobby Shores是我的另一个个人最爱。 但是,在此分析中,Snobby Shores覆盖的大面积肯定会损害其排名。 迅速抢劫该地区的所有战利品箱很困难。 话虽如此,我仍然认为这是一个很好的区域,尤其是在团队比赛中。
战利品箱总数:11

7.咸泉
咸水泉排在第七位。 我不是特别喜欢这个领域,但是每个人都喜欢!
战利品箱总数:15

8.冲洗工厂
冲洗工厂是第八大着陆地点。 最佳路径表明您应该降落在很北的位置,但是我认为从第二个战利品箱开始可能更明智。
战利品箱总数:11

9.孤独小屋
我是孤独小屋的忠实粉丝。 当风暴结束时,从地图的东边返回可让您安全返回地图中心。我要注意的是,此分析也使Lonely Lodge卖了一点时间。 主“ Lodge”中还有更多的战利品箱没有出现在此地图上。 这是由Lonely Lodge的大面积覆盖引起的。
战利品箱总数:9

10.油腻的格罗夫
整理此列表,我们有Greasy Grove。 这个地方非常适合参加团体比赛,并且有大量的战利品供应。
战利品箱总数:12

摘要
有助于确定排名的图表如下所示:

该图表是通过估算玩家的跑步速度,然后确定区域内每个战利品箱的行进时间而制成的。
码
我将简要介绍我在这里使用的Python代码。 我不会深入探讨技术细节,但可以随时向我提问。 代码存储在GitHub上。
获取数据
通过其API从http://www.fortnitechests.info/获取数据。 再次,我对这个网站的创建者感激不尽!
#下载箱子位置
导入 urllib.request,json
DATA_ADDRESS = “ http://www.fortnitechests.info/api/chests”
使用 urllib.request.urlopen(DATA_ADDRESS) 作为 url:
数据= json.loads(url.read()。decode())
数据=数据[ 'lotchests' ]
缩放数据
将数据缩放到0–1而不是0–2000之间。 这使得处理和管理更加容易。 此外,更正地图的“曲率”。 在fortnitechests.info数据中,数据的经度/纬度似乎有些偏差。 我称此为弯曲,但我不完全知道是什么原因造成的。 一个简单的线性方程式解决了这个问题
#将数据缩放到0-1之间
将 numpy 导入 为 np
MAX_DIMENSION = 2000.0 #原始数据的范围可以是0-2000
经度= np.zeros((len(data)))
纬度= np.zeros((len(data)))
X_raw = np.zeros((len(data),2))
对于范围内的我(longitude.shape [0]):
X_raw [i,0] =数据[i] [ 'lng' ]
X_raw [i,1] =数据[i] [ 'lat' ]
#更正X方向上的地图曲线
X_raw [:, 0] = X_raw [:, 0]-(0.0237148 * X_raw [:, 0] + 0.8775403)
X = X_raw / MAX_DIMENSION
绘制数据
该图如下所示
#绘制数据并将其保存到文件
导入 matplotlib.pyplot 作为 plt
从 pylab 导入 savefig
无花果= plt.figure(0)
斧= fig.gca()
ax.set_xticks(np.arange(0,1,0.1))
ax.set_yticks(np.arange(0,1.,0.1))
plt.grid()
plt.scatter(X [:, 0],X [:, 1],color = 'yellow' )
plt.xlim((0,1))
plt.ylim((0,1))
plt.xlabel( '经度' )
plt.ylabel( '纬度' )
plt.title( '要塞宝箱位置' )
plt.savefig( “ rawdata.png” )

初始聚类
使用高斯混合模型来估计初始数据集群。 绘制输出并保存。
#使用高斯混合模型来识别盒子集群
从 sklearn.mixture 导入 GaussianMixture
RANDOM_STATE = 66
N_CLUSTERS = 28
gmm =高斯混合(n_components = N_CLUSTERS,random_state = RANDOM_STATE)
gmm.fit(X)
预测= gmm.predict(X)
平均值= gmm.means_
#绘制找到的聚类中心并覆盖它们
plt.scatter(means [:, 0],means [:, 1],color = 'blue' )
plt.savefig( “ clusters.png” )

离群值去除
从数据集中删除异常值很重要。 它们会大大降低群集的准确性。 我将异常值定义为不在集群中心30秒冲刺之内的任何战利品盒。
#删除不在集群中心30秒内运行的所有集群
从 scipy.spatial.distance 导入 cdist
TIME_SECONDS = 30
UNITS_PER_SECOND = 4.34 #根据经验计算
units_per_second_scaled = UNITS_PER_SECOND / MAX_DIMENSION
半径= units_per_second_scaled * TIME_SECONDS
center_points = np.zeros((N_CLUSTERS,2))
is_within_radius = np.zeros((X.shape [0],N_CLUSTERS))
对于范围内的我(N_CLUSTERS):
距离= cdist(X,np.expand_dims(means [i,:],axis = 0))
is_within_radius [:, i] = np.squeeze(距离<半径,轴= 1)
离群值= np.sum(is_within_radius,axis = 1)== 0
#绘制异常值,然后将其删除
plt.scatter(X [异常值,0],X [异常值,1],color = 'red' )
plt.savefig( “ outliers.png” )
X = X [np.logical_not(outliers),:]
plt.close( 'all' )

KMeans聚类
然后使用KMeans聚类对区域进行更完全的聚类。 图形被绘制并保存。
#使用KMeans聚类来识别数据集上的新聚类
从 sklearn.cluster 导入 KMeans
模型= KMeans(n_clusters = N_CLUSTERS,random_state = RANDOM_STATE)
群集= model.fit_predict(X)
#按颜色绘制数据集群
无花果= plt.figure(1)
斧= fig.gca()
ax.set_xticks(np.arange(0,1,0.1))
ax.set_yticks(np.arange(0,1.,0.1))
plt.grid()
plt.scatter(X [:, 0],X [:, 1],c = clusters)
plt.xlabel( '经度' )
plt.ylabel( '纬度' )
plt.title( “要塞胸甲” )
plt.xlim((0,1))
plt.ylim((0,1))
savefig( “ kmeans_clusters.png” )

最短行程
为了确定最短的旅行距离,我们可以将问题定为“旅行销售人员问题”。 也就是说,我们必须找到访问给定区域中每个战利品盒的最佳路径。 注意,您必须导入在GitHub存储库中找到的“ tsp”代码。 这段代码非常有用,我想感谢/感谢Stack Overflow用户cameronroytaylor提供了这段代码。
#定义一个函数,用于计算相对于预定义路径的两点之间的距离
defcalculate_path_distance (数据,路径):
距离= np.zeros(path.shape [0] -1)
对于范围(1,path.shape [0])中的i:
distance [i-1] = cdist(np.expand_dims(data [path [i],:],axis = 0),np.expand_dims(data [path [i-1],:],axis = 0))
total_distance = np.sum(距离)
返回 total_distance,距离
#导入旅行营业员代码
从 TSP 导入 two_opt
#初始化列表以存储数据
cluster_data_full = []
path_data_full = []
distance_data_full = []
#遍历每个集群并计算最短路径
对于范围(0,N_CLUSTERS)中的我:
cluster_data = X [clusters == i,:]
distance_list = []
data_list = []
path_list = []
total_distance_array = np.zeros(cluster_data.shape [0])
#遍历集群中的每个战利品盒,初始化最短路径问题
#每次迭代都有一个不同的起始战利品盒
对于范围为(0,cluster_data.shape [0])的ii:
rotation_data = np.roll(cluster_data,-ii,axis = 0)
路径= two_opt(rotated_data,0.00001)
total_distance,距离= calculate_path_distance(rotated_data,路径)
total_distance_array [ii] = total_distance
path_list.append(路径)
data_list.append(rotated_data)
distance_list.append(距离)
#确定哪些初始化达到最短路径
minimum_distance = np.argmin(total_distance_array)
#附加集群的最短路径信息
cluster_data_full.append(data_list [minimum_distance])
path_data_full.append(path_list [minimum_distance])
distance_data_full.append(distance_list [minimum_distance])
对区域进行排名
根据60秒内可以收集的战利品数量对每个区域进行排名。 创建本文前面显示的图。
#在60秒内对获得最多箱子的集群进行排名
TIME_THRESHOLD = 60
box_looted_in_threshold = np.zeros((N_CLUSTERS))
对于范围(0,N_CLUSTERS)中的我:
时间= distance_data_full [i] *(1 / units_per_second_scaled)
累积总和= np.cumsum(时间)
box_looted_in_threshold [i] =累积和[cumulative_sum <TIME_THRESHOLD] .shape [0]
#选择TOP_N报告结果
TOP_N = 10
sorted_boxes = np.flip(np.argsort(boxes_looted_in_threshold),axis = 0)
sorted_boxes = sorted_boxes [0:TOP_N]
#选择相关数据
cluster_data_full = [sorted_boxes中为我的[cluster_data_full [i]]]
path_data_full = [针对sorted_boxes中的i的path_data_full [i]]
distance_data_full = [sorted_boxes中我的distance_data_full [i]]
#创建一个选中框与区域花费时间的图表
plt.figure(2)
对于范围(0,TOP_N)中的i:
时间= distance_data_full [i] *(1 / units_per_second_scaled)
累积总和= np.cumsum(时间)
boxs_checked = np.arange(1,time.shape [0] +1)
路径= path_data_full [i]
plt.plot(累加和,boxes_checked)
位置= [ “倾斜式塔楼” , “工厂” , “零售行” , “鬼屋” , “宜人的公园” , “诺比海岸” , “咸水泉” , “冲洗工厂” , “寂寞小屋” , “格罗夫格罗夫” ' ]
plt.title( '已检查框与时间(秒)' )
plt.xlabel( '时间(秒)' )
plt.ylabel( '选中的框' )
plt.legend(位置)
savefig( “ boxescheckedVtime.png” )

在Fortnite地图上绘制数据
创建本文前面显示的最佳路径图。 同样,我想感谢/感谢fortnitechests.info提供了地图资源。
#下载地图文件
从 PIL 导入图片
导入操作系统
#下载世界地图并根据我们的比例调整大小
MAP_ADDRESS = “ http://www.fortnitechests.info/assets/images/web/5KMap.png”
MAP_FILENAME = “ map.png”
如果不是 os.path.isfile(MAP_FILENAME):
urllib.request.urlretrieve(MAP_ADDRESS,MAP_FILENAME)
int_max_dimension = int(MAX_DIMENSION)
地图= Image.open(MAP_FILENAME)
plt.close( 'all' )
map = map.resize((int_max_dimension,int_max_dimension))
对于范围(0,TOP_N)中的i:
plt.close( 'all' )
#选择集群和路径数据并重新缩放
路径= path_data_full [i]
cluster_data_select = cluster_data_full [i]
cluster_data_select = cluster_data_select * MAX_DIMENSION
#翻转y轴
cluster_data_select [:, 0] = cluster_data_select [:, 0]
cluster_data_select [:, 1] =(MAX_DIMENSION-cluster_data_select [:, 1])
#修正y方向的曲率
cluster_data_select [:, 1] = cluster_data_select [:, 1]-(0.02336636 * cluster_data_select [:, 1]-46.31481)
#重新排序数据
reordered_data = cluster_data_select [路径,:]
#绘制数据
MARKER_SIZE = 0.05
LINE_SIZE = 0.15
plt.figure(2 + i)
斧= plt.gca()
plt.imshow(地图)
plt.plot(reordered_data [:, 0],reordered_data [:, 1],lw = LINE_SIZE,c = 'blue' )
ax.scatter(reordered_data [:, 0],reordered_data [:, 1],c = 'yellow' ,s = MARKER_SIZE)
ax.scatter(reordered_data [0,0],reordered_data [0,1],c = 'green' ,s = MARKER_SIZE)
ax.scatter(reordered_data [-1,0],reordered_data [-1,1],c = 'red' ,s = MARKER_SIZE)
plt.axis( 'off' )
plt.title(reordered_data.shape [0])
文件名= str(i)+ '.png'
savefig(文件名,dpi = 1200)
此代码块的输出示例:

结论
我要感谢大家的阅读,并鼓励您对我的发现发表评论和评论。 因为这只是Fortnite(最终是为了娱乐),所以我没有严格调试我的代码。 请不要犹豫指出一个错误! 我计划在Epic更改地图时对此进行更新。