在上一章节的斗兽棋文章发出之后,有老哥在评论区指出,怎么和他们小时候玩的不一样。

这可问到我了,原来你的童年我的童年真的不一样,于是我去百度了一下, 按照百度写的斗兽棋玩法和地图,对我的斗兽棋小APP进行升级,进化为PLUS版本。
规则

UI

在Codding了几天之后,完成了这个Plus版本的斗兽棋,虽然看上去复杂了一些,但是逻辑上比上一期的简易版本更简单了。UI主要分为两部分,地图,棋子
地图
也就是一个 9X7 的格子,获取到屏幕宽度之后,除7,就是每个格子的尺寸了,按照规则图片,在对应的地方画上 水,兽巢,现金,即可。
kotlin
val paint = Paint()
// 河流
paint.color = Color(0xFF03A9F4)
paint.style = PaintingStyle.Fill
val leftRiver = Rect(CellSize.px, CellSize.px * 3, CellSize.px * 3, CellSize.px * 6)
it.drawRect(leftRiver, paint)
val rightRiver =
Rect(CellSize.px * 4, CellSize.px * 3, CellSize.px * 6, CellSize.px * 6)
it.drawRect(rightRiver, paint)
paint.color = Color.Blue
paint.style = PaintingStyle.Stroke
val rect = Rect(0f, 0f, CellSize.px * 7, CellSize.px * 9)
it.drawRect(rect, paint)
// 画竖线
for (row in 1..6) {
it.drawLine(
Offset(CellSize.px * row, 0f),
Offset(CellSize.px * row, CellSize.px * 9),
paint
)
}
// 横线
for (column in 1..8) {
it.drawLine(
Offset(0f, CellSize.px * column),
Offset(CellSize.px * 7, CellSize.px * column),
paint
)
}
val trapImg = ImageBitmap.imageResource(
res = MyApplication.instance.resources,
id = R.drawable.trap
)
// 画陷阱和兽巢
it.drawImage(
trapImg,
topLeftOffset = Offset(
(CellSize.px - trapImg.height) / 2f + CellSize.px * 2,
(CellSize.px - trapImg.height) / 2f
),
paint
)
it.drawImage(
trapImg,
topLeftOffset = Offset(
(CellSize.px - trapImg.height) / 2f + CellSize.px * 3,
(CellSize.px - trapImg.height) / 2f + CellSize.px
),
paint
)
it.drawImage(
trapImg,
topLeftOffset = Offset(
(CellSize.px - trapImg.height) / 2f + CellSize.px * 4,
(CellSize.px - trapImg.height) / 2f
),
paint
)
it.drawImage(
trapImg,
topLeftOffset = Offset(
(CellSize.px - trapImg.height) / 2f + CellSize.px * 2,
(CellSize.px - trapImg.height) / 2f + CellSize.px * 8
),
paint
)
it.drawImage(
trapImg,
topLeftOffset = Offset(
(CellSize.px - trapImg.height) / 2f + CellSize.px * 3,
(CellSize.px - trapImg.height) / 2f + CellSize.px * 7
),
paint
)
it.drawImage(
trapImg,
topLeftOffset = Offset(
(CellSize.px - trapImg.height) / 2f + CellSize.px * 4,
(CellSize.px - trapImg.height) / 2f + CellSize.px * 8
),
paint
)
val homeImg = ImageBitmap.imageResource(
res = MyApplication.instance.resources,
id = R.drawable.home
)
it.drawImage(
homeImg,
topLeftOffset = Offset(
(CellSize.px - homeImg.height) / 2f + CellSize.px * 3,
(CellSize.px - homeImg.height) / 2f
),
paint
)
it.drawImage(
homeImg,
topLeftOffset = Offset(
(CellSize.px - homeImg.height) / 2f + CellSize.px * 3,
(CellSize.px - homeImg.height) / 2f + CellSize.px * 8
),
paint
)
棋子
创建一个二维数组,按照规则的位置,八种动物从小到大,赋值1到8,空地的level为-1,给每个棋子放好位置。
kotlin
private fun initCell(): SnapshotStateList<SnapshotStateList<CellBean>> {
val result = SnapshotStateList<SnapshotStateList<CellBean>>()
for (i in 0 until 9) {
val list = SnapshotStateList<CellBean>()
for (j in 0 until 7) {
val cellBean = CellBean()
list.add(cellBean)
}
result.add(list)
}
result[0][0].isRed = true
result[0][0].level = 7
result[0][6].isRed = true
result[0][6].level = 6
result[1][1].isRed = true
result[1][1].level = 3
result[1][5].isRed = true
result[1][5].level = 2
result[2][0].isRed = true
result[2][0].level = 1
result[2][2].isRed = true
result[2][2].level = 5
result[2][4].isRed = true
result[2][4].level = 4
result[2][6].isRed = true
result[2][6].level = 8
result[6][0].isRed = false
result[6][0].level = 8
result[6][2].isRed = false
result[6][2].level = 4
result[6][4].isRed = false
result[6][4].level = 5
result[6][6].isRed = false
result[6][6].level = 1
result[7][1].isRed = false
result[7][1].level = 2
result[7][5].isRed = false
result[7][5].level = 3
result[8][0].isRed = false
result[8][0].level = 6
result[8][6].isRed = false
result[8][6].level = 7
return result
}
逻辑
逻辑方面,其实很简单,如果按照上一篇文章的思路,就是选中棋子 ,还是操作棋子 。但是如果按照程序来看,我把它按照 点击空白 和 点击棋子 两种方式来写了,这样看上去更为简洁。
点击空白
点击空白的时候,我们要判断三种情况,
1、无效点击
如果当前无被选择棋子,当前的点击就是无效点击
kotlin
val lastIndex = state.value.selectedIndex
val lastCell = getPiece(state.value.selectedIndex)
if (lastIndex == -1) {
//无效点击
return
}
2、无效移动棋子
3、有效移动棋子
2和3就放在一起了
kotlin
if ((lastCell.isRed && index in RedHome) ||
(!lastCell.isRed && index in BlueHome)
) {
// 自家不能进自己屋
return
}
if (lastCell.level == 6 || lastCell.level == 7) {
//狮子老虎可以跨河,特殊处理,上下左右
val access = getAccessCell(lastIndex)
if (index in access) {
//可走
moveOrEat(lastIndex, index)
}
} else {
if (lastIndex % 7 == index % 7 + 1 || lastIndex % 7 == index % 7 - 1 //左右走
|| lastIndex / 7 == index / 7 + 1 || lastIndex / 7 == index / 7 - 1 //往上下走
) {
if (index in River) {
// 是老鼠,可以在河里跑,其它动物均为无效操作
if (lastCell.level == 1) {
moveOrEat(lastIndex, index)
}
} else {
// 不是河,直接走
moveOrEat(lastIndex, index)
}
}
}
首先判断是不是往自己家里面走,自己的棋子不准进自己的兽巢
然后是要对 老虎和狮子 特殊处理,老虎和狮子可以跨河,所以要判断是不是在让他们跨河,以及河流中间是不是有老鼠阻断。我是直接根据老虎或狮子的坐标,获取到它可以走的位置的集合,然后判断当前点击的位置,是不是属于它可以走的位置。
kotlin
private fun getAccessCell(index: Int): IntArray {
val result = intArrayOf(-1, -1, -1, -1)
// 左边能不能到达
if ((index - 1) % 7 != 6) {
// 没有换行
if (index - 1 in River) {
// 有河流,判断能不能跨河
var target = index - 1
var haveMouse = false
while (target in River) {
if (getPiece(target).level == 1) {
//河中间有老鼠
haveMouse = true
break
}
target--
}
if (!haveMouse) {
result[0] = target
}
} else {
// 没有河流,可到达
result[0] = index - 1
}
}
//右边能不能到达
if ((index + 1) % 7 != 0) {
// 没有换行
if (index + 1 in River) {
// 有河流,判断能不能跨河
var target = index + 1
var haveMouse = false
while (target in River) {
if (getPiece(target).level == 1) {
//河中间有老鼠
haveMouse = true
break
}
target++
}
if (!haveMouse) {
result[1] = target
}
} else {
// 没有河流,可到达
result[1] = index + 1
}
}
//下边
if (index + 7 <= 62) {
// 没有越界
if (index + 7 in River) {
// 有河流,判断能不能跨河
var target = index + 7
var haveMouse = false
while (target in River) {
if (getPiece(target).level == 1) {
//河中间有老鼠
haveMouse = true
break
}
target += 7
}
if (!haveMouse) {
result[2] = target
}
} else {
// 没有河流,可到达
result[2] = index + 7
}
}
//上边
if (index - 7 >= 0) {
// 没有越界
if (index - 7 in River) {
// 有河流,判断能不能跨河
var target = index - 7
var haveMouse = false
while (target in River) {
if (getPiece(target).level == 1) {
//河中间有老鼠
haveMouse = true
break
}
target -= 7
}
if (!haveMouse) {
result[3] = target
}
} else {
// 没有河流,可到达
result[3] = index - 7
}
}
return result
}
除了狮子和老虎,其它棋子就判断当前点击的区域是不是上下左右了,如果是的,判断是不是河流,只有老鼠可以进入河流,其它棋子均不行。
点击棋子
同样是三种情况
1、选中或者切换选中棋子
如果目前没有选中,且点击的是自己的棋子,即可选中。或者目前已经有选中了,点击了其它自己的棋子,就是切换选中
kotlin
val lastIndex = state.value.selectedIndex
val lastCell = getPiece(state.value.selectedIndex)
val targetCell = getPiece(index)
if ((lastIndex == -1 && state.value.isRedTurn == targetCell.isRed) || state.value.isRedTurn == targetCell.isRed) {
//选中或者切换选中
state.value = state.value.copy(selectedIndex = index)
return
}
2、吃别人的棋子
3、无效操作
2.3两种操作其实就和走空地差不多了,只是走的时候要判断一下等级
kotlin
if (lastCell.level == 6 || lastCell.level == 7) {
// 豹子老虎可以跨河吃,特殊处理
val access = getAccessCell(lastIndex)
if (index in access) {
//可走
if ((index in BlueTrap)
|| (index in RedTrap)
) {
// 敌方棋子在我方陷阱中,可以吃掉,无视等级,直接吃掉
moveOrEat(lastIndex, index)
} else {
if (lastCell.level >= targetCell.level) {
// 可吃
moveOrEat(lastIndex, index)
}
}
}
} else {
if (lastIndex % 7 == index % 7 + 1 || lastIndex % 7 == index % 7 - 1 //左右走
|| lastIndex / 7 == index / 7 + 1 || lastIndex / 7 == index / 7 - 1 //往上下走
) {
if ((index in BlueTrap)
|| (index in RedTrap)
) {
// 敌方棋子在我方陷阱中,可以吃掉,无视等级,直接吃掉
moveOrEat(lastIndex, index)
} else {
if (lastCell.level >= targetCell.level || (lastCell.level == 1 && targetCell.level == 8)) {
// 可吃
moveOrEat(lastIndex, index)
}
}
}
}
判断结束
在每一步 eatOrMove之后,就要判断游戏是不是结束了
kotlin
private fun moveOrEat(lastIndex: Int, targetIndex: Int) {
getPiece(targetIndex).level = getPiece(lastIndex).level
getPiece(targetIndex).isRed = getPiece(lastIndex).isRed
getPiece(lastIndex).level = -1
state.value = state.value.copy(selectedIndex = -1, isRedTurn = !state.value.isRedTurn)
checkFinish()
}
// 判断结束
private fun checkFinish() {
if (getPiece(RedHome[0]).level != -1) {
// 结束,蓝方赢了
onFinished?.invoke(false)
return
}
if (getPiece(BlueHome[0]).level != -1) {
// 结束,红方胜利
onFinished?.invoke(true)
return
}
var isRedWin = true
var isBlueWin = true
for (index in 0 until 63) {
val piece = getPiece(index)
if (piece.level != -1) {
if (piece.isRed) {
//红的还没死完
isRedWin = false
} else {
isBlueWin = false
}
}
if (!isRedWin && !isBlueWin) {
// 对局还没结束
return
}
}
if (isRedWin) {
onFinished?.invoke(true)
}else{
onFinished?.invoke(false)
}
}
这样,一个Plus版本的斗兽棋就完成了