用compose写个斗兽棋(二)

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

这可问到我了,原来你的童年我的童年真的不一样,于是我去百度了一下, 按照百度写的斗兽棋玩法和地图,对我的斗兽棋小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版本的斗兽棋就完成了

代码地址:github.com/yuuuuke/Bea...

相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone2 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen2 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone2 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui