结对项目:播棋・the・Rock!

结对项目:博客问题清单

Chapter.1 缠结的字节码(Bytecode Complex)

引入
→ 📖 Q1.1§ 请记录下目前的时间。

4.2 20:00

→ 📖 Q1.2§ 根据之前学习的专业课和各类开发经验,回答:为什么原生应用相较 Web app 更有快的印象?这样的“速度”又是如何达成的?

因为原生应用不需要再运行时进行解释或编译,其在编译时会被优化为目标平台的机器码,而且原生应用可能能够更好地利用设备的硬件,或者更好地利用os的特性,比如能有更高效的图形渲染等,所以原生应用的速度可能感觉会更快。

→ 📖 Q1.3§ “要让 Web 生态、或者说一种雄心勃勃的软件运行环境的未来,受益于既有的‘原生’软件开发模式、技术栈和工具链,我们还缺少什么?”——你们的答案是?

缺少一种很强大,很方便的工具链移植工具。比如说,缺少一种可以将各种应用在原生应用上的工具移植到web开发上的工具。或者说即便没有这种transform工具,那也可以针对web开发一套独属于web的方便的开发工具链。更具体地来说,用原生工具进行 Web 开发事实上面临着如下的几个问题:

  • 便捷稳定的集成:目前Web平台和原生应用之间存在一定的隔阂。要实现无缝集成,我们需要更好的互操作性,这包括但不限于API的统一、数据格式的标准化以及协议的互通。
  • 性能优化和资源管理
    虽然Web技术在性能上取得了显著进步,但与原生应用相比仍有差距。我们需要进一步的技术创新来缩小这一差距,特别是在图形渲染、计算密集型任务处理、内存管理和电池寿命优化等方面。WebAssembly是一个积极的方向,但我们还需要更多工具和方法来优化Web应用的性能和资源使用。
  • 技术栈和开发工具调试支持:原生开发生态系统拥有一系列成熟的IDE、调试工具和性能分析工具。Web开发者同样需要这样一套全面的工具链来支持复杂应用的开发、调试和性能优化。同时WebAssembly 的编译技术事实上仍不够便捷,编译过程中出现的问题仍然会消耗开发者大量精力(比如这次实验将 cpp 文件编译至 web 端过程中出现了许多问题)
结对过程
→ 📖 Q1.4(I) 作为本项目的调查:请如实标注在开始项目之前对 Wasm 的熟悉程度分级,可以的话请细化具体的情况。

I. 没有听说过;

II. 仅限于听说过相关名词;

III. 听说过,且有一定了解;

IV. 听说过,且使用 Wasm 实际进行过开发(即便是玩具项目的开发)。

没有听说过

→ 📖 Q1.5§ 请在完成任务的同时记录,并在完成任务后整理完善:

完成编程任务期间,你和你的搭档依次做了什么,比如:

  1. 查阅了什么资料;

播棋技术方面:查了一些关于播棋的文献,在各种自媒体平台上的一些关于播棋的教学等。
环境配置方面:遇到一些报错,在csdn,博客园,github等代码社区做了搜索,也利用各大搜索引擎(baidu, google, bing等)查资料以求解决报错
基础学习方面:在菜鸟教程初步学习js的基本语法

  1. 如何进行了开发;

一方面,一个人写了c程序,。

另一方面,另一个人一边学习js语法,写了中间js文件mid.js,用来读入并调用wasm函数。

  1. 遇到了什么问题,又通过什么方式解决。

主要是遇到了环境的问题

  • emcc编译cpp出现了难以解决的报错,然后从cpp换成了c成功解决了报错
  • T3要求返回数组的函数出现了报错,询问了GPT类似于如何在js中正确接收c编译来的wasm中函数返回的数组的问题后成功解决。
编程语言选择

以下问题根据你选择的编程语言回答:例如,如果你选择 AssemblyScript,你只需要回答标记为 Q1.6.A.x 的问题。

自选的编程语言
→ 📖 Q1.6.X.1§ 选择该编程语言的原因是?

选择C语言的原因是因为对其比较熟悉,不像rust和assemblyScript,用c的话几乎完全不用付出语言的学习成本。而且环境对c比较友好(用cpp就会出现难以解决的报错)。

→ 📖 Q1.6.X.2§ 为了完成相关编程任务,进行了哪些操作、使用了怎样的工具链?简要描述相关工具做了什么。

具体的操作步骤如下:

  • 新建.c文件写好满足要求的函数
  • 用命令 emcc xxxx.c -o xxx.js -s EXPORTED_FUNCTIONS="['_bocchiShutUp']" -s EXPORTED_RUNTIME_METHODS="['cwrap']"生成.c文件对应的.wasm文件
  • 编写mid.js程序,作为中间人,完成一系列操作:读入.wasm文件,拿到文件中的函数,给函数传参执行函数,返回执行后的结果。将这一系列操作封装成js函数,并且将该js函数export
  • 在课程组提供的test.jsimport {xxx} from "mid.js";,即可实现在test.js中调用c函数的目的
  • 执行npm run submit-test

其中,用到的工具有:emcc, npm(c语言编译器这种非常常用的工具便不赘述了)
其中,emcc用来将.c程序翻译为.wasm程序;npm用来执行测试。

总结
→ 📖 Q1.7§ 请记录下目前的时间。

4.3 0:30

→ 📖 Q1.8(I) 请写下本部分的心得体会。

第一题是结对项目的开始,函数的要求是真的很简单,20行c代码即可搞定,但是刚开始配置环境,还是非常头疼的,遇到不明所以的报错查半天资料也难以解决,最后还是请教同学才得以解决报错(非常感谢同学的帮助)。

Chapter.2 石子翻滚,晨光降临你身(Rock’n Roll, Morning Light Falls on You)

结对过程
→ 📖 Q2.1§ 请记录下目前的时间。

4.3 14:00

→ 📖 Q2.2§ 请在完成任务的同时记录,并在完成任务后整理完善:
  1. 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;

针对任务二,本团队基于 PSP 2.1 修改的 PSP 表格进行任务耗时的估计

Personal Software Process Stages个人软件开发流程预估耗时(分钟)实际耗时(分钟)
PLANNING计划10
- Estimate- 估计这个任务需要多少时间10
DEVELOPMENT开发230
- Analysis & Design Spec- 需求分析 & 生成设计规格(确定要实现什么)20
- Technical Background- 了解技术背景(包括学习新技术)20
- Coding Standard- 代码规范10
- Design- 具体设计(确定怎么实现)10
- Coding- 具体编码100
- Code Review- 代码复审20
- Test Design- 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例)10
- Test Implement- 测试实现(设计/生成具体的测试用例、编码实现测试)40
REPORTING报告50
- Quality Report- 质量报告(评估设计、实现、测试的有效性)20
- Size Measurement- 计算工作量10
- Postmortem & Process Improvement Plan- 事后总结和过程改进计划(总结过程中的问题和改进点)20
TOTAL合计290
  1. 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);

查了有关播棋的实战视频,加深对播棋规则的理解,减少之后编写程序出bug的可能性。

两个人都理解题意写一份满足题目要求的函数(其中有一个人写的是和数据生成器逻辑耦合的函数,相当于一边写了一个数据生成器,一边也有了可以与另一份逻辑对拍的逻辑),写好之后进行对拍(写数据生成器来生成各种类型的数据),尽可能确保对拍程序的正确性。

由于上一个任务在环境方面做的铺垫,导致本任务没有遇到什么难以解决的问题,只是c程序写的有bug在被测试出来之后进行了简单的debug。

测试
→ 📖 Q2.3§ 请说明针对该任务,你们设计和实现测试的方法及过程,包括但不限于:出于对需求的哪些考虑设计了哪些测试用例、如何评估所设计测试的有效性 等等。

设计测试:几乎没有太多考虑,由于上oo课和做oo助教时写自动评测机的经验,很自然地就想到了设计一个数据生成器来自动化生成数据进行评测。

实现测试:用cpp写了数据生成器,数据生成器中也会有一套题目要求函数的核心逻辑的实现,写好输出生成器之后,用数据生成器中的逻辑去生成数据,并且数据生成器会给一个expected result,将这个期望值和另一位同学实现的c语言函数的实际输出结果进行对拍。

数据生成器逻辑设置
分为三种数据,包括:刚好下完一局,下了一半尚未结束,存在不合法操作的数据。其中第三者又分为:

  • 已经下完了还继续下
  • 没有下完继续下,但是存在操作次序错
  • 不存在操作次序错,但是操作棋格错(操作了空格子,或者操作了计分洞)
    通过这三种分类应该几乎可以覆盖所有情况的数据。数据生成的效果图如下:

请添加图片描述

有效性
我们认为我们的测试有着一定的效果。首先从顶层策略上,我们的数据生成器尽可能地考虑了所有可能的情况,然后在细节生成上,我们充分利用随机数进行数据生成,通过大规模随机测试可以尽可能做到无死角的覆盖。两个不同的人写的逻辑进行对拍,没有问题后可以确保两个人的程序逻辑相同,确保相同后又和其他同学的程序进行了简单的对拍,在这种情况下仍存在bug的可能性应该较小。

→ 📖 Q2.4(I) 请说明单元测试对软件开发的作用。

单元测试可以帮助开发者验证代码的正确性,确保代码按照预期的方式工作。通过编写测试用例覆盖代码的各个部分,可以及早发现代码中的错误和逻辑缺陷,从而提高代码的质量和稳定性。通过保证各个单元的正确性进而保证整个系统的正确性。

总结
→ 📖 Q2.5§ 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。

当前时间:4.3 17:30

Personal Software Process Stages个人软件开发流程预估耗时(分钟)实际耗时(分钟)
PLANNING计划1015
- Estimate- 估计这个任务需要多少时间1015
DEVELOPMENT开发230270
- Analysis & Design Spec- 需求分析 & 生成设计规格(确定要实现什么)2020
- Technical Background- 了解技术背景(包括学习新技术)2040
- Coding Standard- 代码规范1010
- Design- 具体设计(确定怎么实现)1010
- Coding- 具体编码100120
- Code Review- 代码复审2020
- Test Design- 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例)1010
- Test Implement- 测试实现(设计/生成具体的测试用例、编码实现测试)4040
REPORTING报告5050
- Quality Report- 质量报告(评估设计、实现、测试的有效性)2020
- Size Measurement- 计算工作量1010
- Postmortem & Process Improvement Plan- 事后总结和过程改进计划(总结过程中的问题和改进点)2020
TOTAL合计290335
→ 📖 Q2.6(I) 请写下本部分的心得体会。

由于第一题的铺垫,第二题在环境方面的问题可以说是畅通无阻。又由于有编写数据生成器的经验,所以在测试方面也没有付出太大的学习成本,这部分主要是让我第一次对播棋的规则有了一个完整的认识,总的来说这本部分进行地比较顺利。

Chapter.3 十二进制的黄昏(Dusk of the Duodecimal System)

结对过程
→ 📖 Q3.1§ 请记录下目前的时间。

4.6 9:00

→ 📖 Q3.2§ 请在完成任务的同时记录,并在完成任务后整理完善:
  1. 浏览任务要求,参照 附录A:基于 PSP 2.1 修改的 PSP 表格,估计任务预计耗时;

针对任务三,本团队基于 PSP 2.1 修改的 PSP 表格进行任务耗时的估计

Personal Software Process Stages个人软件开发流程预估耗时(分钟)实际耗时(分钟)
PLANNING计划20
- Estimate- 估计这个任务需要多少时间20
DEVELOPMENT开发470
- Analysis & Design Spec- 需求分析 & 生成设计规格(确定要实现什么)40
- Technical Background- 了解技术背景(包括学习新技术)40
- Coding Standard- 代码规范20
- Design- 具体设计(确定怎么实现)20
- Coding- 具体编码200
- Code Review- 代码复审40
- Test Design- 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例)30
- Test Implement- 测试实现(设计/生成具体的测试用例、编码实现测试)80
REPORTING报告80
- Quality Report- 质量报告(评估设计、实现、测试的有效性)40
- Size Measurement- 计算工作量10
- Postmortem & Process Improvement Plan- 事后总结和过程改进计划(总结过程中的问题和改进点)30
TOTAL合计570
  1. 完成编程任务期间,依次做了什么(比如查阅了什么资料,随后如何进行了开发,遇到了什么问题,又通过什么方式解决);

在完成任务的过程中,我们查阅了播棋相关的常见策略,包括了强化学习、基于Alpha-Beta剪枝的minmax策略树等。

主要参考资料包括:

  1. https://blog.csdn.net/weixin_43441742/article/details/126457295
  2. Design of Artificial Intelligence for Mancala Games. Prof. Pier Luca Lanzi
  3. Divilly, Colin; O’Riordan, Colm; Hill, Seamus (2013). [IEEE 2013 IEEE Conference on Computational Intelligence and Games (CIG) - Niagara Falls, ON, Canada (2013.08.11-2013.08.13)] 2013 IEEE Conference on Computational Inteligence in Games (CIG) - Exploration and analysis of the evolution of strategies for Mancala variants. , (), 1–7. doi:10.1109/cig.2013.6633628
    根据我们选定的 c 语言的编程特性,由于 c 语言并不像 python 提供了全面完善的机器学习库,因此我们放弃了使用强化学习的想法,最终采用了基于Alpha-Beta剪枝的minmax策略树进行开发。

开发过程中遇到的主要问题可以分为以下几点:

  • 如何平衡计算开销和策略性能
  • 如何做到对代码的高效复用
  • 如何设置评价函数的权重

如何平衡计算开销和策略性能的问题上,我们参考了exploration and analysis of the evolution of strategies for Mancala variants一文中关于minmax策略搜索层度的探讨,并进行了多次实验,发现搜索层数大于10后程序性能显著降低,最终决定采用 4 作为程序在决策树中的搜索深度。

对代码的高效复用的问题上,我们参考了T2中对于播棋规则的模拟,封装了isGameOver(int seq[])Evaluate(int seq[], int player)makeMove(int seq[], int move, int curPlayer)等函数,一定程度上尽量复用了此前的代码(尽管有些地方由于数据结构存储不同又重新写了一遍)

如何设置评价函数的权重的问题上,我们参考了 Design of Artificial Intelligence for Mancala Games 一文中的权重设置:

  • H1(最左坑中的棋子数)的权重是0.198649。
  • H2(玩家一侧的棋子总数)的权重是0.190084。
  • H3(非空坑的数量)的权重是0.370793。
  • H4(玩家的得分)的权重是1。
  • H5(从最右边的坑移动的棋子)的权重是0.418841。
  • H6(对手得分的最小化)的权重是0.565937。

请添加图片描述

代码可复用性与需求变更
→ 📖 Q3.3§ 请说明针对该任务,你们对 🧑‍💻 T2 中已实现的代码进行了哪些复用和修改。

首先,在makeMove(int seq[], int move, int curPlayer)中,参考🧑‍💻 T2 中对于播棋每一步演替过程的模拟,实现了代码复用,该函数输入了某一时刻的棋牌序列,选择移动的棋洞编号,以及当前执行操作的玩家编号,输出操作后的棋盘状态以及下一步进行操作的玩家编号。本质上来说,这一个函数就是用程序语言对播棋规则进行刻画,这正是🧑‍💻 T2任务的核心部分,因此实现了代码的复用。

其次,在isGameOver(int seq[])中,同样完成了对🧑‍💻 T2代码的复用,该函数本质上说是进行某一时刻棋盘状态是否终止的判断,同样是用程序语言对播棋规则进行刻画,恰好对应了🧑‍💻 T2代码中每次循环中进行了合法性检验和循环终止判断。所以在 T2 的代码上,对数据结构进行了一定的修改和迁移,尽量避免了大规模的内存开销,最大程度上实现了代码的复用。

其三,在🧑‍💻 T2代码中本身用 board 数组存储了某个时刻的棋盘状态,而在该任务中由于需要输出一个数组来表示当前状态下的棋盘,因此可以直接对 board 数组进行维护,最终需要输出时将 board 数组尾部加上数据位即可实现T3-1的要求。

代码修改方面,主要是输入逻辑与合法性检验上进行了修改,由于 T3-1 保证了仅最后一步可能非法且 flag 的含义表征为最后一步应该执行操作的玩家编号,所以在🧑‍💻 T2代码的基础上,适当删去了部分合法性检验,同时加上了对最后一步操作的特判。同时由于输出的数据位不同,在输出逻辑也进行了一定的代码修改以适应新的要求。

→ 📖 Q3.4(I) 请说明在编码实现时,可以采取哪些设计思想、考虑哪些设计冗余,来提高既存代码适应需求变更的能力。

这感觉就是要秉承着高内聚和低耦合的思想。功能1对应一块逻辑,功能2对应一块逻辑,各个不同的功能对应的逻辑要区分开来,而不要惨杂在一起(也有模块化设计的思想)。

需求建模和算法设计
→ 📖 Q3.5§ 请说明你们如何建模这一需求。

需求为:对于一个确定的棋盘,某一方应该走哪一格,才能实现对自己的利益最大化。

对此需求进行建模:

  • 定义输入:status数组表示棋盘状态,flag表示此时应该哪一方做出决定
  • 决策搜索:需要写算法接收当前输入,探索所有可能做出的决策。(在本题中很简单,所有有子的格子就是所有可以做出的决策)
  • 决策评估:需要有算法,对搜索到的决策进行评估判断,评估其对自己而言的收益大小。
  • 做出决策:根据评估结果,从可能的决策中选择一个对自己利益最大的决策,下一步便做此决策。
→ 📖 Q3.6§ 请说明针对该任务,你们采取了哪些策略来取得更大的净胜棋数,如何编程实现这些策略。

主要策略

  • 启发式评估函数:我们设计了一个启发式评估函数Evaluate,该函数通过计算特定的游戏状态特征和相应的权重来评估棋盘状态。通过结合这些特征和权重,Evaluate函数能够为给定的棋盘状态提供一个评分,指导决策过程中的移动选择。具体来说:
    • H1-H6为不同的游戏状态特征,包括玩家的计分坑(得分)中的棋子数量、玩家一侧的棋子总数、能够直接移到对方计分坑中的棋子数量、非空坑的数量、最左侧坑中的棋子数、和最右侧非空坑中的棋子数。
    • W1-W6为每个特征的权重,这些权重经过精心设计,以便更准确地反映游戏状态对玩家胜利的贡献。
  • Alpha-Beta剪枝的Minimax算法:为了提高决策效率并深入搜索可能的游戏状态,我们实现了带有Alpha-Beta剪枝的Minimax算法。这一算法能够减少必须评估的节点数,从而在更深的层次上探索游戏状态,而不会因计算量过大而受限。通过递归地调用minmax函数,算法探索了从当前状态出发的所有可能的移动,并选择了能够最大化玩家利益的最佳移动。
  • 选择最优移动:最终,mancalaOperator函数基于Minimax算法和启发式评估函数的输出,为当前玩家选择一个能够最大化预期净胜棋数的移动。通过遍历所有可能的移动并评估它们的结果,函数能够确定最有利的移动策略。
    编程实现
  • 启发式评估:通过Evaluate函数实现,根据游戏状态计算得分。
  • 深度搜索与决策:通过minmax函数实现,使用Alpha-Beta剪枝来优化搜索。
  • 移动和游戏规则处理:通过makeMove函数实现,精确模拟棋子的移动和游戏规则。
  • 选择最优移动mancalaOperator函数综合评估结果和搜索算法,决定最佳行动。
软件度量
→ 📖 Q3.7§ 请说明你们如何量度所实现的程序模块的有效性,例如:“如何说明我们的程序模块对弈能力很强?”,尝试提出一些可能的定量分析方式。

我们度量的方式就是和其他同学的博弈逻辑进行pk,以及和播棋手游上的AI进行pk。

我们提出一种可能的定量分析方式:

找到足够多的对手(可以是其他同学的程序,可以是各种播棋手游,播棋小程序中匹配到的网友,或者是不同难度的人机模式)作为测试集,对测试集中的每位对手进行pk,记录下来胜负的次数,计算出和每位对手pk的胜率,用胜率乘以该对手的水平评分(最初以[1, 100]为范围,对测试集中的对手水平打分),表示和这位对手pk时的得分,将和测试集中所有对手pk的得分进行累加,得到的结果就是本程序的水平得分。这个“水平得分”即反映程序的博弈性能。

总结
→ 📖 Q3.8§ 请记录下目前的时间,并根据实际情况填写 附录A:基于 PSP 2.1 修改的 PSP 表格 的“实际耗时”栏目。

当前时间:4.6 17:30(中间有吃饭的时间)

Personal Software Process Stages个人软件开发流程预估耗时(分钟)实际耗时(分钟)
PLANNING计划2030
- Estimate- 估计这个任务需要多少时间2030
DEVELOPMENT开发470600
- Analysis & Design Spec- 需求分析 & 生成设计规格(确定要实现什么)4040
- Technical Background- 了解技术背景(包括学习新技术)4080
- Coding Standard- 代码规范2020
- Design- 具体设计(确定怎么实现)2030
- Coding- 具体编码200260
- Code Review- 代码复审4040
- Test Design- 测试设计(确定怎么测,比如要测试哪些情景、设计哪些种类的测试用例)3030
- Test Implement- 测试实现(设计/生成具体的测试用例、编码实现测试)80100
REPORTING报告8080
- Quality Report- 质量报告(评估设计、实现、测试的有效性)4040
- Size Measurement- 计算工作量1010
- Postmortem & Process Improvement Plan- 事后总结和过程改进计划(总结过程中的问题和改进点)3030
TOTAL合计570710
→ 📖 Q3.9(I) 请写下本部分的心得体会。

本次任务具有一定的难度,尽可能地去提高程序的播棋水平对我们来说并不是一件很容易的事情。而且播棋其实不算是一门很火热的游戏,所以网上有关播棋的资料也并不多。这极大地考验了我们的信息检索能力。除此之外,写一个播棋版的“阿尔法狗”我觉得是一件非常有趣的事。

结对项目总结

结对过程回顾和反思
→ 📖 Q4.1§ 提供两人在讨论的结对图像资料。

在这里插入图片描述

→ 📖 Q4.2§ 回顾结对的过程,反思有哪些可以提升和改进的地方。
  • 最后的对弈算法有待进一步改进,因为我们在和其他同学的作品对弈的过程中发现确实下不过一些同学的策略。
  • 有的任务在一起做时可能不够特别的“结对”,有的细节部分可能还是以明确的分工过程完成的,而不是说两个人一起讨论完成,这一点可能有待进一步改进
→ 📖 Q4.3(I) 锐评一下你的搭档!并请至少列出三个优点和一个缺点。

优点:

  • qy很认真,很有卷力,愿意花费较多的时间在作业上
  • 非常易于沟通交流,点赞!
  • 心态良好,情绪稳定,面对暂时难以解决的问题和各种bug不急不躁!!

缺点:

  • 效率有待提高
对结对编程的理解
→ 📖 Q4.4(I) 说明结对编程的优缺点、你对结对编程的理解。

优点:

  • 两个人相互配合,有助于减少bug的产生,提高代码的质量。
  • 遇到问题时,两个人共同思考解决,有助于更快地找到bug所在的位置。

缺点:

  • 两名开发者如果产生分歧,可能会引发矛盾,破坏人际关系,导致项目进度延缓
  • 适用范围有限,有的项目可能更适合独立完成而不是结对完成,对于这种情况如果非要结对的话反而效率不会高。
代码仓库
→ 📖 Q4.5§ 请提供你们完成代码实现的代码仓库链接。

https://github.com/KnightQuay/BUAASE2024-PairProgramming/tree/dev_homework

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值