结对编程作业
https://github.com/Eden8771/pokergame
| 人员 | 分工 | 博客链接 |
|---|---|---|
| wang | 无 | https://www.cnblogs.com/eden8771/p/15455564.html |
一、原型设计
设计说明
https://modao.cc/app/36dafb537ffa7adc978b023d31334d0b248995f8 《扑克牌游戏》
- 采用墨刀进行原型设计开发
- 游戏共有三个界面,分为首页界面,大厅界面和游戏界面
- 首页界面由游戏标题和三个模式选择的按钮组成,分为联机对战,双人对战和人机对战三种游戏模式

游戏界面
规则介绍

遇到的困难及解决方法
- 在原型设计过程中,选择了墨刀作为原型设计工具,墨刀操作简单且美观,适合作为原型工具。
- 在创建过程中,一开始不知道可以将界面的几个元件打包成一个母版,导致在旋转界面时异常的麻烦,后通过打包成母版解决了这个问题
- 通过此次原型设计,学会了先分析需求,再构想页面,最终通过原型开发工具设计出符合需求的页面,熟悉了原型设计的过程,且对开发工具的使用更加熟练了。
二、原型设计实现
代码实现思路(UI逻辑部分)
从功能角度分析分为三个板块
- 联机对战
- 双人对战
- 人机对战
通过对需求的分析, 因此确定了几个区域:
- 牌堆区
- (出牌)放置区
- 敌我手牌区
- 托管区(或者说需要给这个托管按钮找个位置)
也正是对于敌我手牌区的分析, 知道手牌经常会超过一定的数量. 因此选择了横屏作为显示的方式
由于界面的一致性(仅相差被关掉的托管按钮), 因此后文剩余的两个模式的界面将不再展示
- 在左下角设定了一个机器人+按钮的形式作为托管的开关
- 在右上角设定了"设置"按钮用于调节音量 (因为有配欢乐的BGM斗地主经典音效)
- 中心区域为牌堆区与(出牌)放置区
- 左上角为对局状态显示
- 敌我手牌区分立两边
此时通过接口, 在对方未加入时, 左上角的"对局状态"则会显示对局未开始
当对局开始后, 也就是接收到相应的msg后, 就会相应显示 你的回合/对方回合
只有在自己的回合才会显示 "出牌"/"翻牌" 的按钮. 防止玩家误触
为了防止误触的还有在返回时的确认选项:
代码实现思路(代码逻辑部分)
代码组织与内部实现设计.
- 通过各类
repository仓库类对不同类型的方法进行了封装, 方便使用, 例如:
UIRepository封装了有关UI方面的方法RobotRepository则封装了有关机器人出牌方面的算法的方法
- 通过对
package的命名来进行各种类的分类
算法的关键
/**
* 人机返回当前应该出的牌
* String:
* 返回牌型则表示该出的牌, 返回空字符串则表示翻牌
* 算法优先级:
* ---------------------------------
* x:我方手牌 y:对手手牌 z:放置区叠的牌
* --------------------------------
* 1. 必胜条件 (满足后只需要不断翻牌即可获得胜利):
* x + 2y + z >= 78
* 2. 牌库仅剩一张时, 假设手牌中存在该花色牌且当前为机器人回合, 且放置区顶非该花色牌. 则打出该花色牌.
* 3. x + z < 5 直接翻牌
* 4. x + 1 + z < y 直接翻牌
* 5. x > 5 或 z > 5 只出牌
* 5.1 无牌可打, 翻牌
* 5.2 有牌可打
* 5.2.1 优先出手牌中花色数量最多的牌
* --------------------------------
有价值的代码片段
- UI代码
private fun refresh(last: GetGameUUIDLast) {
if (last.last_code != "") {
val code = last.last_code.split(" ")
val player = code[0]
val operation = code[1]
val card = code[2]
if (operation == "0") {
if (outCards.size > 0 && card[0] == outCards.peek()[0]) {
outCards.push(card)
val host = intent.getBooleanExtra("host", false)
if (player == "0" && host || player == "1" && !host) {
for (c in outCards) {
myCards.add(c)
myCardCount += 1
}
} else if (player == "1" && host || player == "0" && !host) {
for (c in outCards) {
enemyCards.add(c)
enemyCardCount += 1
}
}
outCards.clear()
binding.curCardActivityGame.setImageResource(R.drawable.transparentcard)
} else {
outCards.push(card)
binding.curCardActivityGame.setImageResource(UIRepository.cards[card]!!)
}
} else if (operation == "1") {
if (outCards.size > 0 && card[0] == outCards.peek()[0]) {
val host = intent.getBooleanExtra("host", true)
outCards.push(card)
if (player == "0" && host || player == "1" && !host) {
for (c in outCards) {
myCards.add(c)
myCardCount += 1
}
} else if (player == "1" && host || player == "0" && !host) {
for (c in outCards) {
enemyCards.add(c)
enemyCardCount += 1
}
}
outCards.clear()
binding.curCardActivityGame.setImageResource(R.drawable.transparentcard)
} else {
outCards.push(card)
binding.curCardActivityGame.setImageResource(UIRepository.cards[card]!!)
val host = intent.getBooleanExtra("host", true)
if ((player == "0" && host) || (player == "1" && !host)) {
myCards.remove(card)
myCardCount -= 1
} else if ((player == "1" && host) || (player == "0" && !host)) {
enemyCards.remove(card)
enemyCardCount -= 1
}
}
}
}
binding.myCardsActivityGame.removeAllViews()
binding.enemyCardsActivityGame.removeAllViews()
if (myCards.size > 0) {
myCardCount = 0
for (c in myCards)
addMyCard(c)
binding.myCountActivityGame.text = myCardCount.toString()
}
if (enemyCards.size > 0) {
enemyCardCount = 0
for (c in enemyCards)
addEnemyCard(c)
binding.enemyCountActivityGame.text = enemyCardCount.toString()
}
if (isRobot.value == true) {
isRobot.value = true
}
}
fun getRobotCard(x: List<String>, y: List<String>, z: Stack<String>): String {
Log.d("z", z.toString())
val totalPile = ArrayList(UIRepository.cards.keys)
val curPile = totalPile.clone() as ArrayList<String>
curPile.removeAll(x)
curPile.removeAll(y)
curPile.removeAll(z)
when {
x.isEmpty() -> {
return ""
}
curPile.size == 1 -> {
return when {
x.size + 1 + z.size < y.size -> ""
z.size > 0 -> whenPileOnlyOne(curPile, x, z.peek()[0])
else -> whenPileOnlyOne(curPile, x, ' ')
}
}
x.size + 2 * y.size + z.size >= 78 -> {
return ""
}
x.size + z.size <= 5 -> {
return ""
}
x.size + 1 + z.size <= y.size -> {
return ""
}
x.size > 5 -> {
return if (z.isNotEmpty() && judgeSuitExists(z.peek()[0], x)) {
if (z.size > 0)
getMostCardFromHand(x, z.peek()[0])
else
getMostCardFromHand(x, ' ')
}
else {
getMostCardFromHand(x, ' ')
}
}
z.size > 5 -> {
return if (judgeSuitExists(z.peek()[0], x)) {
if (z.size > 0)
getMostCardFromHand(x, z.peek()[0])
else
getMostCardFromHand(x, ' ')
}
else {
getMostCardFromHand(x, ' ')
}
}
else -> {
return ""
}
}
}
传入的参数为上文注释中的x,y,z. 通过对三个参数的分析.
根据自己指定的算法, 希望能够得出一个出牌的较优解.
展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路
单元测试代码:
class ExampleUnitTest {
/**
* 人机返回当前应该出的牌
* String:
* 返回牌型则表示该出的牌, 返回空字符串则表示翻牌
* 算法优先级:
* ---------------------------------
* x:我方手牌 y:对手手牌 z:放置区叠的牌
* --------------------------------
* 1. 必胜条件 (满足后只需要不断翻牌即可获得胜利):
* x + 2y + z >= 78
* 2. 牌库仅剩一张时, 假设手牌中存在该花色牌且当前为机器人回合, 且放置区顶非该花色牌. 则打出该花色牌.
* 3. x + z < 5 直接翻牌
* 4. x + 1 + z < y 直接翻牌
* 5. x > 5 或 z > 5 只出牌
* 5.1 无牌可打, 翻牌
* 5.2 有牌可打
* 5.2.1 优先出手牌中花色数量最多的牌
* --------------------------------
*牌的花色 D方块 S黑桃 H红桃 C梅花
*/
/**
* 测试必胜规则,此时只翻牌
*/
@Test
fun test1() {
val x = ArrayList<String>()
val y = ArrayList<String>()
val z = Stack<String>()
x.addAll(listOf("D1","D2","D3","D4","D5","D6","D7"))
y.addAll(listOf("DJ","DQ","DK","S4","S5","S6","S7","S8","S9","H1","H2","H4","C5","C7","C8","C9","CJ","C10","C1","HK","H7","C4","C6","C2","D10","D9","H6","HK","HQ","H10","H3","D8","S2"))
z.addAll(listOf("S3","S4","S5","S9","S10","SK"))
assertEquals("",RobotRepository.getRobotCard(x, y, z))
}
/**
* 测试规则2,此时打出牌库中剩下唯一一张花色的牌
*/
@Test
fun test2() {
val x = ArrayList<String>()
val y = ArrayList<String>()
val z = Stack<String>()
x.addAll(listOf("D1","D3","S10","SJ","SQ","S1","D2","S3","D4","C3","D5","D6","D7","CQ","S7","S8","S9","H1","H2","H4","C5","C7","C8","C9","CJ","C10","C1","HK","H7","C4","C6","C2"))
y.addAll(listOf("DJ","DQ","DK","S4","S5","S6","D10","D9","H6","HJ","HQ","H10","H3","D8","S2"))
z.addAll(listOf("CK","H5","H8","H9"))
assertEquals("S10",RobotRepository.getRobotCard(x, y, z))
}
/**
* 测试规则3,此时x + z < 5,直接翻牌
*/
@Test
fun test3() {
val x = ArrayList<String>()
val y = ArrayList<String>()
val z = Stack<String>()
x.addAll(listOf("H1","C2","D3"))
y.addAll(listOf("H4","S10"))
z.addAll(listOf("HJ"))
assertEquals("",RobotRepository.getRobotCard(x, y, z))
}
/**
* 测试规则4,此时应直接翻牌
*/
@Test
fun test4() {
val x = ArrayList<String>()
val y = ArrayList<String>()
val z = Stack<String>()
x.addAll(listOf("HJ","C2","SK","D6","H6"))
y.addAll(listOf("DK","S4","S5","S6","S7","S8","S9","H1","H2","H4","C5"))
z.addAll(listOf("H8","C10","D1","C1"))
assertEquals("",RobotRepository.getRobotCard(x, y, z))
}
/**
* 测试规则5,此时x > 5 或 z > 5 选择出牌,且有牌可打符合5.2.1,选择出手牌中与放置区顶不相同的牌打出
*/
@Test
fun test5() {
val x = ArrayList<String>()
val y = ArrayList<String>()
val z = Stack<String>()
x.addAll(listOf("C9","H3","HJ","C2","SK","D6","H6"))
y.addAll(listOf("DK","S4","S5","S6","S7","S8","S9","H1","H2","H4","C5"))
z.addAll(listOf("C10","D1","C1","H8"))
assertEquals("C9",RobotRepository.getRobotCard(x, y, z))
}
/**
* 测试6,此时x > 5 或 z > 5 选择出牌,但无牌可打,符合5.1,选择翻牌
*/
@Test
fun test6() {
val x = ArrayList<String>()
val y = ArrayList<String>()
val z = Stack<String>()
x.addAll(listOf())
y.addAll(listOf("DK","S4","S5","S6","S9","H1","H2","H4","C5"))
z.addAll(listOf("H8","C10","D1","C1","S7","S8","HK","H7","C4","C6","C2","D10"))
assertEquals("",RobotRepository.getRobotCard(x, y, z))
}
/**
* 测试7,此时x > 5 或 z > 5 选择出牌,且有牌可打符合5.2.1,选择出手牌中与放置区顶不相同的牌打出
*/
@Test
fun test7() {
val x = ArrayList<String>()
val y = ArrayList<String>()
val z = Stack<String>()
x.addAll(listOf("C9","H3","HJ","C2","SK","D6","H6"))
y.addAll(listOf("DK","S4","S5","S6","S7","S8","S9","H1","H2","H4","C5"))
z.addAll(listOf("CQ","H8","C10","D1","C1"))
assertEquals("H3",RobotRepository.getRobotCard(x, y, z))
}
/**
* 测试8,此时x > 5 或 z > 5 选择出牌,且有牌可打符合5.2.1,选择出手牌中与放置区顶不相同的牌打出
*/
@Test
fun test8() {
val x = ArrayList<String>()
val y = ArrayList<String>()
val z = Stack<String>()
x.addAll(listOf("C9","C2","H3","HJ",
