COMA(二):Counterfactual Multi-Agent Policy Gradients 论文讲解

Counterfactual Multi-Agent Policy Gradients

论文链接:https://arxiv.org/pdf/1705.08926.pdf

1. 问题提出(解决了什么问题?)


在现实世界中,有非常多的问题需要多个单位之间的“合作”才能完成任务,这就需要学习一种非中心式策略的控制系统,即每个agent有着属于自己的决策大脑,而非靠拥有全局信息的决策系统下达指令(毕竟有时候全局的信息量过于庞大,并且agent到中心网络的通信不一定每时每刻都稳定,因此中心式的决策系统很难实现)。因此,该论文提出了一种方法用于学习非中心式的部分可观测的多智能体协同的控制策略。
COMA利用全局评价网络(critic)来评价Q值,利用非全局行为网络(actor)来决定agent的行为。由于在训练时使用的是全局网络进行评价,并且采用参数共享的方式,使得agent能够在做行为选择的时候参考其他agent的状态再做决定,这就加入了“协同”的功能。


2. 介绍

该论文分为以下三个部分:

  • 提出传统的RL算法在协同任务中不足

若使用传统的RL算法来解决多智能体的问题,则会存在以下三个不足之处:

  1. 输入的action space应该是所有agent的联合动作空间(joint action space),这个空间会随着agent数量增加而增加。
  2. 此外,由于部分可观测性(即单个agent在某一时刻只能观测到部分环境的信息,无法获得全局信息,比如一个小兵只能看到视野范围内的地图信息,视野外的地图信息是无法观测的),使得agent在做决策时只能依照自己当前的部分观测信息(local observation),没有与其他agent进行信息共享的能力。
  3. 使用联合动作空间获得的reward是来自所有agent采取的所有action共同得到的reward,这就很难知道每一个agent的action应该得到的多少子回报,这就是原文中提到的 “Individual Reward Assignment”。
  • COMA中的主要思想

COMA是一种基于actor-critic的变种方法,其中actor是依照critic评估出来的梯度值进行更新学习的。整个算法共有三个比较核心的思想:

  1. 学习过程中会有一个中心式评价网络, 这个网络主要用于对actor选择的决策进行好坏评价以此来教会actor如何做一个好的决策。为什么称为中心式的网络?这是因为该网络可以获取场景中的全局信息,包括所有agent在这一时刻采取的行为信息观测信息。但是,单个agent在利用actor做行为选择时只能依照自身的当前观测信息和经历过的历史信息进行决策,做决策时是无法获得全局信息的。这种方式被称为“中心式评价,边缘式决策”。
  2. COMA引入了一个概念叫做 “反事实准则(counterfactual baseline)” ,这个概念是整篇论文的重点。为了解决 Individual Reward Assignment 的问题,反事实准则提出,每个agent应该拥有不同的reward,这样才能知道在这一次的全局行为决策中单个agent的action贡献是多少。而单个agent的reward通过两个值计算得来:当前情况下的全局reward和将该agent行为替换为一个默认行为后的全局reward。可以这样理解:该回报值其实计算的是Agent aaa采取行为 uuu 会比采取默认行为 cac_aca 要更好(DaD^aDa > 0)还是更坏(DaD^aDa < 0)。这个特定agent特定动作reward就被称为counterfactual baseline,COMA使得每一个agent的每一个action都有一个自身的counterfactual baseline。
  3. 如上面所说,每一个agent的每一个动作都会有一个counterfactual baseline,如果要计算出所有动作的baseline,就需要把每一个行为替换成 ‘默认行为’ 并与环境互动得到一个reward。当agent数目很多且联合动作空间很大的时候,这种方法显然是不可取的。因此,COMA提出:使用中心critic网络来estimate每一个动作的Q值,来代替与环境交互后得到的reward
  • 验证场景及其结果分析


3. 背景

3.1 数学建模

论文中将多智能体协同任务想象成一个随机决策的游戏,这个游戏 GGG 包含以下几个因素:
G=<S,U,P,r,Z,O,n,γ>G = <S, U, P, r, Z, O, n, \gamma> G=<S,U,P,r,Z,O,n,γ>
其中,

  • S→S \quad \rightarrow \quadS 环境状态集: ∀s∈S\forall s \in SsS.

  • U→U \quad \rightarrow \quadU 所有动作样本空间:在每一时刻,每个agent采取一个行为 uta∈Uu_t^a \in UutaU,并组成联合动作空间 u∈U\textbf{u} \in UuU.

  • P→P \quad \rightarrow \quadP 状态转移函数:根据当前状态sss和联合动作空间u\textbf{u}u,计算一时刻状态s′s'sP(s′∣s,u)P(s'|s, \textbf{u})P(ss,u).

  • r→r \quad \rightarrow \quadr 全局回报值:r(s,u)r(s, \textbf{u})r(s,u).

  • Z→Z \quad \rightarrow \quadZ 局部观测集:单个agent在每一时刻有一个局部观测 z∈Zz \in ZzZ.

  • O→O \quad \rightarrow \quadO 局部观测函数:Agent aaa 的局部观测 zzz 是根据全局环境信息 sss 通过 OOO 函数计算得来,z=O(s,a)z = O(s, a)z=O(s,a).

  • n→n \quad \rightarrow \quadn agent的个数,共有 nnn 个.

  • γ→\gamma \quad \rightarrow \quadγ 折扣因子,用于指定计算未来回报时的衰减强弱.

此外,每个agent有一个 action-observation 的历史记录 τa\tau^aτa,actor在做决策的时候是基于历史信息做的决策 πa(ua∣τa)\pi^a(u^a|\tau^a)πa(uaτa). 其实这里基于历史记录做决策可以理解为:之前在做update决策网络参数的时候,是基于之前的历史信息做的更新,所以用更新后的actor去做决策就可以看作是记住了历史经验后做的决策了。

3.2 基本概念回顾

这里在回顾一下DQN中的一些基本概念,后续内容会用的到:

累计回报Rt=∑l=0∞γlrt+lR_t = \sum_{l=0}^\infty{\gamma^lr_{t+l}}Rt=l=0γlrt+l,其中 γ\gammaγ 是折扣因子;
评价函数分为两个:对当前状态的评价函数 Vπ(st)V^\pi(s_t)Vπ(st),对当前状态下当前联合动作空间的评价函数 Qπ(st,ut)Q^\pi(s_t, u_t)Qπ(st,ut);
Vπ(st)=E[Rt∣st]Qπ(st,ut)=E[Rt∣st,ut]V^\pi(s_t) = E[R_t|s_t] \qquad Q^\pi(s_t, \textbf{u}_t) = E[R_t|s_t, \textbf{u}_t] Vπ(st)=E[Rtst]Qπ(st,ut)=E[Rtst,ut]
优势函数Aπ(st,ut)=Qπ(st,ut)−Vπ(st)A^\pi(s_t, \textbf{u}_t) = Q^\pi(s_t, \textbf{u}_t) - V^\pi(s_t)Aπ(st,ut)=Qπ(st,ut)Vπ(st).

Policy Gradient :Value-Based中主要使用的更新方法——梯度上升法,梯度 ggg 可以表示为:
g=∑t=0TRt▽θπlogπ(ut∣st)g = \sum_{t=0}^TR_t\bigtriangledown_{\theta^\pi}log\pi(u_t|s_t) g=t=0TRtθπlogπ(utst)

关于Actor-Critic模型:

AC模型中,actor是根据critic所求得的梯度来进行学习的。因为RtR_tRt是一个期望值,无法求得精确的值,因此需要用其他的表达式来近似替代RtR_tRt。替代RtR_tRt一共有两种方式:

  1. 优势函数法:使用 Q(st,ut)−b(st)Q(s_t, u_t) - b(s_t)Q(st,ut)b(st) 来代替 RtR_tRt,其中 bbb 为一个基准值,用于保证所有action的Q值有正有负,通常可以用 V(st)V(s_t)V(st) 来代替 bbb 值。也就是用 Qπ(st,ut)−Vπ(st)=A(st,ut)Q^\pi(s_t, u_t) - V^\pi(s_t) = A(s_t, u_t)Qπ(st,ut)Vπ(st)=A(st,ut) 来代替 RtR_tRt
  2. TD法:使用 rt+γV(st+1)−V(st)r_t + \gamma V(s_{t+1}) - V(s_t)rt+γV(st+1)V(st) 来代替 RtR_tRt

如何训练中心评价网络critic:

在这篇论文中,作者训练了一个中心评价网络fc(⋅,θc)f^c(·, \theta^c)fc(,θc),网络参数为 θc\theta^cθc,使用一种稍微改变了下的TD法进行学习——TD(λ)TD(\lambda)TD(λ)nnn 步的reward值进行综合来得到一个平均值 Gt(n)=∑l=1nγl−1rt+l+γnfc(⋅t+n,θc)G_t^{(n)} = \sum_{l=1}^n\gamma^{l-1}r_{t+l} + \gamma^nf^c(·_{t+n}, \theta^c)Gt(n)=l=1nγl1rt+l+γnfc(t+n,θc)。使用梯度下降的方法来更新网络参数θc\theta^cθcLtL_tLt表示ttt时刻的损失函数:
Lt(θc)=(y(λ)−fc(⋅t,θc))2L_t(\theta^c) = (y^{(\lambda)} - f^c(_{·t}, \theta^c)) ^ 2 Lt(θc)=(y(λ)fc(t,θc))2
其中:

y(λ)=(1−λ)∑n=1∞λn−1Gt(n)Gt(n)=∑l=1nγl−1rt+l+γnfc(⋅t+n,θc)y^{(\lambda)} = (1-\lambda)\sum_{n=1}^\infty \lambda^{n-1}G_t^{(n)} \\ G_t^{(n)} = \sum_{l=1}^n\gamma^{l-1}r_{t+l} + \gamma^nf^c(·_{t+n}, \theta^c) y(λ)=(1λ)n=1λn1Gt(n)Gt(n)=l=1nγl1rt+l+γnfc(t+n,θc)

因此,整个公式也可以表示为:

Lt(θc)=((1−λ)∑n=1∞λn−1(∑l=1nγl−1rt+l+γnfc(⋅t+n,θc))−fc(⋅t,θc))2L_t(\theta^c) = ((1-\lambda)\sum_{n=1}^\infty \lambda^{n-1}(\sum_{l=1}^n\gamma^{l-1}r_{t+l} + \gamma^n {\color{red}f^c(·_{t+n}, \theta^c)}) - f^c(·_{t}, \theta^c)) ^ 2 Lt(θc)=((1λ)n=1λn1(l=1nγl1rt+l+γnfc(t+n,θc))fc(t,θc))2

Note:公式中一共有两个 fc(⋅,θc)f^c(·, \theta^c)fc(,θc) 网络,但是前一个fc()f^c()fc()是estimate出来的目标值 y(λ)y^{(\lambda)}y(λ),为了加快模型的收敛速度,第一个的 fc()f^c()fc() 中的θc\theta^cθc 应该被fix住(式子中的红色部分),若干个steps后再被update,这和target network的思路是一样的。


4. 算法分析

4.1 Independent Actor-Critic

IAC方法指每一个agent学习一个独立的actor-critic,在这篇论文中采用参数共享的方法,使得所有agent共用一个actor和一个critic。在学习的时候,critic只能根据agent自身的local observation进行估计值,并且也只能估计该agent的单个动作uau^aua的效用,而不是联合动作空间u\textbf{u}u的效用。
论文中对传统的IAC算法有两处改变:

  1. 在估计V值时,每个 agent 的 critic 估计的是V(τa)V(\tau^a)V(τa),估计的是这个agent历史action-observation数据的效用值,而不是传统的当前状态的效用值V(st)V(s_t)V(st)VVV评价网络基于TD(λ)TD(\lambda)TD(λ)方法进行梯度更新,见上面。
  2. 在估计Q值时,每个agent的critic估计的是Q(τa,ua)Q(\tau^a, u^a)Q(τa,ua), 也是基于action-observation的历史数据对当前行为uau^aua进行效用估计。QQQ评价网络是通过梯度下降优势函数A(τa,ua)A(\tau^a, u^a)A(τa,ua)来进行学习的,其中优势函数的定义为:单个动作产生的Q值减去所有动作产生的Q值,即 A(τa,ua)=Q(τa,ua)−V(τa)A(\tau^a, u^a) = Q(\tau^a, u^a) - V(\tau^a)A(τa,ua)=Q(τa,ua)V(τa)。其中V(τa)V(\tau^a)V(τa)定义为:在已知"动作-观测"历史数据下,所有动作产生的效用总和,即V(τa)=∑uaπ(ua∣τa)Q(τa,ua)V(\tau^a) = \sum_{u^a}\pi(u^a|\tau^a)Q(\tau^a, u^a)V(τa)=uaπ(uaτa)Q(τa,ua)

IAC的缺陷在于,训练时只能依据单个agent的局部观测和单个action的效用评定,这样很难学出一套好的协同策略。

4.2 Counterfatual Multi-Agent Policy Gradient

COMA的主要思想有三个:中心式评价网络,使用反事实准为每一个行为分配不同的reward值,高效计算每一个不同的reward值,下面对每一个思想进行介绍讲解。

  • Center critic

在IAC算法中,训练评价网络时只用到了单个agent的history τa\tau^aτa。既然这个评价网络只会在训练的时候使用,那么我们完全可以把全局状态 sss 输入用于训练,若全局观测不可获得,则将当前所有agent的"action-observation"的历史记录 τ\tauτ代替全局状态 sss,如下图所示:

图中,每一个Actor都会给出此刻的决策行为 utu_tut,并且环境也会给出此时环境的全局信息 sts_tst 以及此刻的回报值 rtr_trt

一种很简单的方式是直接使用TD-Error来进化这个网络:
g=▽θπlogπ(u∣τta)(r+γV(st+1)−V(st))g = \bigtriangledown_{\theta_\pi}log\pi(u|\tau_t^a)(r+\gamma V(s_{t+1}) - V(s_t)) g=θπlogπ(uτta)(r+γV(st+1)V(st))
但是,这样的方法不能解决 Individual Reward Assignment 的问题,因为TD算出来的Reward是一个全局Reward ,无法推算出每一个action的单独Reward值。为此,论文提出了"反事实准则"。

  • Counterfatual baseline

反事实准则(Conuterfatual Baseline)允许为不同的action独立分配一个不同的独立reward。这个独立reward DaD^aDa 需要根据当前情况下的全局reward将该agent行为替换为一个'默认行为'后的全局reward两个值进行计算,
Da=r(s,u)−r(s,(u−a,ca))D^a = r(s, \textbf{u}) - r(s, (\textbf{u}^{-a}, c_a)) Da=r(s,u)r(s,(ua,ca))
其中,u−a\textbf{u}^{-a}ua 代表联合动作空间除去当前Agent aaa 这一时刻采取的行为。(u−a,ca)(\textbf{u}^{-a}, c_a)(ua,ca) 代表当前Agent aaa 采取"默认行为" cac_aca 后所有Agent的联合动作空间。在学习过程中,agent会想办法最大化回报值 DaD^aDa,这其实就是在想办法最大化全局的reward r(s,u)r(s, \textbf{u})r(s,u),因为式子的后项跟agent当前采取什么行为是没有关系的。关于DaD^aDa这个式子可以这样理解:回报值DaD^aDa其实计算的是Agent aaa采取行为 uuu 会比采取默认行为 cac_aca 要更好(DaD^aDa > 0)还是更坏(DaD^aDa < 0)
这个想法是正确的,但是要想计算出每一个动作的DaD^aDa值,就需要将每个动作都替换成默认行为cac_aca去与环境互动一次得到最终结果,这样采样次数会非常多;此外,默认行为的选取也是无法预测的,到底选择哪一个行为当作默认行为才是最合适的也是比较难决定的。因此,文中提出使用"函数拟合"的方式来计算DaD^aDa

前面提到,中心评价网络可以评价一个联合动作空间 u\textbf{u}u 在一个状态 sss 下的 QQQ 值。由于默认行为很难定义,于是我们把采取 “默认行为” 得到的效用值近似为采取一个Agent “所有可能行为” 的效用值总和。因此,DaD^aDa 就可以用以下等式进行计算:
Aa(s,u)=Q(s,u)−∑ua′πa(u′a∣τa)Q(s,(u−a,u′a))A^a(s, \textbf{u}) = Q(s, \textbf{u}) - \sum_{u_a'}\pi^a(u'^a|\tau^a)Q(s, (\textbf{u}^{-a}, u'^a)) Aa(s,u)=Q(s,u)uaπa(uaτa)Q(s,(ua,ua))
其中,Aa(s,u)A^a(s, \textbf{u})Aa(s,u) 就是 DaD^aDa 的等效近似。

  • Efficient evaluation of baseline

尽管baseline的方式解决了独立回报的问题,但是如果要建立一个网络,接收s,us, \textbf{u}s,u两个输入,输出为所有agent的所有action的话,那么输出神经元的个数就等于 ∣U∣n|U|^nUn(n个agent有|U|个动作)。当agent数目很多或动作空间很大的时候就会造成输出层无法实现。为此,COMA构造了一种网络,该网络接收 ut−a,st,ota,a,ut−1−a\textbf{u}^{-a}_t, s_t, o_t^a, a, \textbf{u}^{-a}_{t-1}uta,st,ota,a,ut1a 等参数,输出为Agent aaa 每一个action的Q-value值,输出维度由 ∣U∣n|U|^nUn 降到了 ∣U∣|U|U ,如下图所示。

5. 实验

5.1 实验场景

该论文使用星际争霸游戏作为实验场景,让算法控制的小队和游戏AI控制的小队进行对战,并计算胜率。算法小队中,每一个作战单位都由一个去中心式的controller进行控制。为了提升游戏难度,比赛加入了 “部分可观测” 条件的限制,视野范围等于攻击范围。这意味着当敌人没有进入攻击范围内时,作战单位是不知道敌人位置信息的,因此agent不仅要学会如何去探索敌方目标,还需要与队友共享敌方目标的位置信息。为了证明加入 “部分可观测” 限制后问题变得有多复杂,作者做了实验对比:当不加入观测限制时,普通的启发式算法能够到达98%的胜率;加入限制后,胜率降到了66%。

5.2 动作空间及回报值

每个agent都有着相同的动作空间:{ move[direction],attack[enemy_id],stop,noop }。
一次Episode下的全局Reward为:对敌人造成的伤害 DeD_eDe 减去我方受到的伤害 DuD_uDu 除以2,此外,若击毁一个敌方单位获得10点的回报,即:

R(t)=De−Du2+10nR(t) = D_e - \frac{D_u}{2} + 10n R(t)=De2Du+10n
其中,nnn为摧毁敌方单位数。若有一方将敌方团灭,则获得的回报值为全队剩余血量+200。

5.3 状态特征

算法一共有两个网络:actor 和 critic。其中actor接收agent的局部观测信息;critic接收全局状态信息。

  • 局部观测信息

由于作战单位的视野范围等于攻击范围,因此观测到的视野是以该单位为中心的一个圆。局部观测信息是指在视野圆圈内,每一个单位(包括敌方和友方)的以下几个信息:distance,relative x,relative y,unit type,shield。其中 unit type 是指作战单位是何种兵种,shield是为了模拟平A的冷却(因为t切割的很小,每一时刻Agent都有可能选择平A这个动作,所以在平A还没有冷却好的时候,敌方单位会套上一个无敌的盾来抵消这一次平A受到的伤害)。
疑问:观测范围中的单位数量应该是随时间变化而变化的,但网络的输入层神经元应该是固定的,怎么解决变长的问题?(未解决)

  • 全局观测信息

全局观测信息包含了所有单位的relative x,relative y,unit type,shield,healthy point,cooldown信息,其中 relative 的坐标信息是相对据地图中心的相对坐标,不再是针对于某一个特定目标的坐标。

  1. 喂给独立回报计算网络(Q-function critic,用于计算单个动作独立回报)的数据包括全局观测信息 + 被评估agent此刻局部观测信息
  2. 喂给中心评价网络(center critic,用于评估全局状态V(st)V(s_t)V(st))的数据包括全局观测信息 + 全部agent此刻局部观测信息
5.4 实验结果

实验结果如下图所示,其中3m,5m分别指代一个作战小队中包含3个,5个marine(一种兵种);2d_3z指代一个作战小队中包含2条龙和3个狂热者。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/290399.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

lecture6-mini批量梯度训练及三个加速的方法

Hinton的第6课&#xff0c;这一课中最后的那个rmsprop&#xff0c;关于它的资料&#xff0c;相对较少&#xff0c;差不多除了Hinton提出&#xff0c;没论文的样子&#xff0c;各位大大可以在这上面研究研究啊。 一、mini-批量梯度下降概述 这部分将介绍使用随机梯度下降学习来训…

Dapr集成之GRPC 接口

Dapr 为本地调用实现 HTTP 和 gRPC API 。通常大家第一时间想到的是通过 gRPC 调用 Dapr&#xff0c;更重要的一点是Dapr 也可以通过 gRPC 与应用程序通信。要做到这一点&#xff0c;原理很简单&#xff0c;应用程序需要托管一个gRPC服务器&#xff0c;并实现 Dapr 的GRPC 规范…

jQuery Validate验证框架详解

jQuery Validate验证框架详解 jQuery校验官网地址&#xff1a;http://bassistance.de/jquery-plugins/jquery-plugin-validation 一、导入js库 <script type"text/javascript" src"<%path %>/validate/jquery-1.6.2.min.js"></script> &…

RNN入门笔记

本笔记来源自Youtube李宏毅老师的RNN视频归纳&#xff0c;主要分为以下几个知识点: RNN 的特点RNN 的几种实现方法 (Simple RNN, LSTM)RNN 的训练不稳定性RNN 的keras实现 (定长和变长输入案例) Recurrent Neural Network Feature of RNN Differ from normal Neural Networ…

WPF 基础控件之 DatePicker 样式

此群已满340500857 &#xff0c;请加新群458041663由于微信群人数太多入群请添加小编微信号yanjinhuawechat 或 W_Feng_aiQ 邀请入群需备注WPF开发者 PS&#xff1a;有更好的方式欢迎推荐。支持NugetInstall-Package WPFDevelopers.Minimal -Version 3.2.001—代码如下一、创建…

stagefright框架(四)-Video Buffer传输流程

這篇文章將介紹Stagefright中是如何和OMX video decoder传送buffer。 (1) OMXCodec會在一開始的時候透過read函式來傳送未解碼的data給decoder&#xff0c;並且要求decoder將解碼後的data傳回來 status_t OMXCodec::read(...){ if (mInitialBufferSubmit) { mInitialBuffe…

微信支付四大支付模式分别有哪些区别?

微信支付是集成在微信客户端的支付功能&#xff0c;用户可以通过手机完成快速的支付流程。微信支付已为百货、餐厅、便利店、酒店、快递、景区、医院、售货机等提供了支付与营销的全方位支持。 目前微信支付已实现刷卡支付、扫码支付、公众号支付、APP支付&#xff0c;并提供企…

利用Deep Reinforcement Learning训练王者荣耀超强AI

Mastering Complex Control in MOBA Games with Deep Reinforcement Learning&#xff08;一&#xff09;知识背景&#xff08;二&#xff09;系统架构&#xff08;三&#xff09;算法结构3.1 Target Attention3.2 利用LSTM学习技能连招释放3.3 Decoupling of Control Dependen…

C和指针之编译出现warning: implicit declaration of function ‘matrix_multiply‘ is invalid in C99问题

1、问题 在我的mac上编译一个c文件&#xff0c;出现下面错误2、原因和解决办法 是因为我用vim的时候&#xff0c;把函数名少写了一个字符导致&#xff0c;把这个函数名改正就行了。

5. 堪比JMeter的.Net压测工具 - Crank 实战篇 - 接口以及场景压测

1. 前言通过之前的学习&#xff0c;我们已经掌握了crank的配置以及对应http基准工具bombardier、wrk、wrk2的用法&#xff0c;本篇文章介绍一下如何将其用于实战&#xff0c;在实际的项目中我们如何使用crank来完成压测任务。2. 项目背景目前有一个项目&#xff0c;我们希望通过…

Pytorch快速入门笔记

Pytorch 入门笔记1. Pytorch下载与安装2. Pytorch的使用教程2.1 Pytorch设计理念及其基本操作2.2 使用torch.nn搭建神经网络2.3 创建属于自己的Dataset和DataLoader2.3.1 编写Dataset类2.3.2 编写Transform类2.3.3 将Transform融合到Dataset中去2.3.4 编写DataLoader类2.4 使用…

详解用65行javascript代码做Flappy Bird

点击查看特效JavaScript做Flappy Bird游戏&#xff0c;代码仅仅65行资源包括&#xff1a;javascript源码&#xff1a;phaser.min.js&#xff1b;main.js&#xff1b;index.html素材&#xff1a;两张图片&#xff01;素材PS&#xff1a;素材源码下载来我的前端群570946165&#…

C和指针之数组编程练习5 (矩阵相乘)

1、问题 5.如果A是个x行y列的矩阵,B是个y行z列的矩阵,把A和B相乘,其结果将是另一个x行z列的矩阵C。这个矩阵的每个元素是由下面的公式决定的: 例如: 结果矩阵中14这个值是通过2-2加上-6-3得到的,编写一个函数,用于执行两个矩阵的乘法。函数的原型如下: void matrix_mul…

我的技术回顾因ABP框架触发DevOps云原生之路-2020年

我的技术回顾&#xff1a;2015年&#xff1a;我的技术回顾那些与ABP框架有关的故事-2015年2016年&#xff1a;从ABP框架国内社区发展回顾.NET技术变迁-2016年2017年&#xff1a;我的技术回顾那些与ABP框架有关的故事-2017年2018年&#xff1a;我的技术回顾那些与ABP框架有关的故…

半身头像

画的好丑。。。继续加油 转载于:https://www.cnblogs.com/manlurensheng/p/4102631.html

Swift - 操作SQLite数据库(引用SQLite3库)

SQLite轻量级数据库在移动应用中使用非常普遍&#xff0c;但是目前的库是C编写的&#xff0c;为了方便使用&#xff0c;对SQLite相关的操作用Swift进行了封装。这个封装代码使用了一个开源项目SQLiteDB&#xff0c;地址是&#xff1a;https://github.com/fahimf/sqlitedb 重要事…

如何在Clion中使用C++调用Python代码

在很多时候&#xff0c;我们需要在一个c工程项目中调用部分Python代码&#xff0c;这就需要我们实现Python和C之间的交互。交互方式有两种&#xff1a;1. 依靠 TCP 建立的网络通信交互&#xff1b;2. 嵌入式混合语言编程&#xff08;Embedding Code&#xff09;。这里主要介绍后…

.NET6之MiniAPI(二十四):用Polly重试

为了保障系统的稳定和安全&#xff0c;在调用三方服务时&#xff0c;可以增加重试和熔断。重试是调用一次失败后再试几试&#xff0c;避免下游服务一次闪断&#xff0c;就把整个链路终止&#xff1b;熔断是为了防止太多的次数的无效访问&#xff0c;导致系统不可知异常。Polly是…

CLion 中使用 C++ 版本的 OpenCV

配置环境&#xff1a; Windows 10CLion 2020OpenCV 3.4.1MinGW-w64 1. 下载 CLion 并配置好 MinGW CLion 下载地址&#xff1a;https://www.jetbrains.com/clion MinGW 安装包下载地址&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1c00uHbcf_jGeDDrVg99jtA 提取码&…

如何理解 C# 中的 System.Void 类型?

咨询区 ordag我知道方法声明成 void 表示不返回什么东西&#xff0c;但我发现在 C# 中 void 不仅仅是一个关键词&#xff0c;而且还是一个真实的类型。void 是 System.Void 的别名&#xff0c;就像 int 的别名是 System.Int32 一样&#xff0c;但为什么不允许直接使用Void类型呢…