Kotlin实现斗牛游戏的完整代码与解析
斗牛作为一款经典的纸牌游戏,凭借简单易懂的规则和富有乐趣的竞技性深受大众喜爱。本文将为大家展示一个完整的Kotlin实现版本,并解析其核心逻辑。
一、代码实现解析
1. 生成扑克牌组
首先需要生成一副完整的扑克牌,包含4种花色(黑桃♠、红桃♥、方块♦、梅花♣)和13种点数(A、2-10、J、Q、K):
kotlin
fun generateDeck(): List<String> {
val suits = listOf("♠", "♥", "♦", "♣")
val ranks = listOf("A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K")
return suits.flatMap { suit -> ranks.map { rank -> "$suit$rank" } }
}
这段代码通过flatMap将花色和点数组合,生成如"♠A"、"♥10"、"♦K"等格式的牌面字符串。
2. 牌面点数转换
游戏中需要将牌面转换为对应的点数,规则如下:
- A算1点
- J、Q、K算10点
- 其他牌按数字本身算点
kotlin
fun cardToPoint(card: String): Int {
return when (val rank = card.substring(1)) {
"A" -> 1
"J", "Q", "K" -> 10
else -> rank.toInt()
}
}
3. 核心判定逻辑
斗牛游戏的核心在于判断手牌的"牛"等级,从高到低依次为:
五小牛
五张牌的点数都小于5,且总和不超过10:
kotlin
fun isFiveLittleCow(points: List<Int>): Boolean {
return points.all { it < 5 } && points.sum() <= 10
}
五花牛
五张牌都是10点牌(10、J、Q、K):
kotlin
fun isFiveFlowerCow(points: List<Int>): Boolean {
return points.all { it == 10 }
}
牛牛
五张牌中任意三张之和为10的倍数,剩余两张之和也为10的倍数:
kotlin
fun isNiuNiu(points: List<Int>): Boolean {
for (i in points.indices) {
for (j in i + 1 until points.size) {
for (k in j + 1 until points.size) {
val sum3 = points[i] + points[j] + points[k]
if (sum3 % 10 == 0) {
val remainingPoints = points.filterIndexed { idx, _ -> idx != i && idx != j && idx != k }
val sum2 = remainingPoints.sum()
if (sum2 % 10 == 0) {
return true
}
}
}
}
}
return false
}
普通牛(牛一到牛十)
五张牌中任意三张之和为10的倍数,剩余两张之和除以10的余数即为牛的等级:
kotlin
fun checkNiuNiu(hand: List<String>): String {
val points = hand.map { cardToPoint(it) }
if (isFiveLittleCow(points)) {
return "五小牛"
}
if (isFiveFlowerCow(points)) {
return "五花牛"
}
if (isNiuNiu(points)) {
return "牛牛"
}
// 检查普通牛
for (i in hand.indices) {
for (j in i + 1 until hand.size) {
for (k in j + 1 until hand.size) {
val sum3 = points[i] + points[j] + points[k]
if (sum3 % 10 == 0) {
val remainingPoints = points.filterIndexed { idx, _ -> idx != i && idx != j && idx != k }
val sum2 = remainingPoints.sum()
val niuValue = sum2 % 10
return if (niuValue == 0) "牛十" else "牛$niuValue"
}
}
}
}
return "无牛"
}
4. 胜负判定
将各种牛等级转换为数值,数值高的获胜:
kotlin
fun getNiuValue(niuStr: String): Int {
return when (niuStr) {
"五小牛" -> 13
"五花牛" -> 12
"牛牛" -> 11
"牛十", "牛10" -> 10
"牛九", "牛9" -> 9
// ... 其他牛等级
else -> 0 // 无牛
}
}
fun judgeWinner(bankerNiu: String, playersNiu: List<String>): List<String> {
val results = mutableListOf<String>()
val bankerValue = getNiuValue(bankerNiu)
playersNiu.forEach { playerNiu ->
val playerValue = getNiuValue(playerNiu)
when {
playerValue > bankerValue -> results.add("赢")
playerValue < bankerValue -> results.add("输")
else -> results.add("平局 庄家赢")
}
}
return results
}
5. 游戏主流程
kotlin
fun main() {
// 生成并洗牌
val deck = generateDeck().shuffled(Random)
// 发牌:庄家5张,3个闲家各5张
val bankerHand = deck.take(5)
val player1Hand = deck.subList(5, 10)
val player2Hand = deck.subList(10, 15)
val player3Hand = deck.subList(15, 20)
// 显示手牌
println("庄家的手牌: ${bankerHand.joinToString(" ")}")
println("闲家一手牌: ${player1Hand.joinToString(" ")}")
// ... 显示其他玩家手牌
// 计算牛等级
val bankerNiu = checkNiuNiu(bankerHand)
val player1Niu = checkNiuNiu(player1Hand)
// ... 计算其他玩家牛等级
// 判定胜负
val results = judgeWinner(bankerNiu, listOf(player1Niu, player2Niu, player3Niu))
}
二、完整代码实现
以下是基于Kotlin实现的斗牛游戏完整代码,包含了从牌组生成、发牌到判定胜负的全流程:
kotlin
package com.zzj.www
import kotlin.random.Random
fun main() {
// 生成并洗牌
val deck = generateDeck().shuffled(Random)
// 发牌:庄家5张,3个闲家各5张
val bankerHand = deck.take(5)
val player1Hand = deck.subList(5, 10)
val player2Hand = deck.subList(10, 15)
val player3Hand = deck.subList(15, 20)
// 显示各家手牌
println("庄家的手牌: ${bankerHand.joinToString(" ")}")
println("闲家一手牌: ${player1Hand.joinToString(" ")}")
println("闲家二手牌: ${player2Hand.joinToString(" ")}")
println("闲家三手牌: ${player3Hand.joinToString(" ")}\n")
// 计算各家的牛型
val bankerNiu = checkNiuNiu(bankerHand)
val player1Niu = checkNiuNiu(player1Hand)
val player2Niu = checkNiuNiu(player2Hand)
val player3Niu = checkNiuNiu(player3Hand)
// 显示牛型结果
println("庄家: $bankerNiu")
println("闲家1: $player1Niu")
println("闲家2: $player2Niu")
println("闲家3: $player3Niu")
// 判定胜负
val results = judgeWinner(bankerNiu, listOf(player1Niu, player2Niu, player3Niu))
println("\n判定结果:")
results.forEachIndexed { idx, result ->
println("闲家${idx + 1} $result")
}
}
/**
* 生成一副完整的扑克牌
* 包含4种花色(♠, ♥, ♦, ♣)和13种点数(A, 2-10, J, Q, K)
*/
fun generateDeck(): List<String> {
val suits = listOf("♠", "♥", "♦", "♣")
val ranks = listOf("A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K")
return suits.flatMap { suit -> ranks.map { rank -> "$suit$rank" } }
}
/**
* 将牌面转换为对应的点数
* A=1,J/Q/K=10,其他按数字本身计算
*/
fun cardToPoint(card: String): Int {
return when (val rank = card.substring(1)) {
"A" -> 1
"J", "Q", "K" -> 10
else -> rank.toInt()
}
}
/**
* 判断是否为五小牛
* 五张牌的点数都小于5,且总和不超过10
*/
fun isFiveLittleCow(points: List<Int>): Boolean {
return points.all { it < 5 } && points.sum() <= 10
}
/**
* 判断是否为五花牛
* 五张牌都是10点牌(10、J、Q、K)
*/
fun isFiveFlowerCow(points: List<Int>): Boolean {
return points.all { it == 10 }
}
/**
* 判断是否为牛牛
* 任意三张牌之和为10的倍数,剩余两张之和也为10的倍数
*/
fun isNiuNiu(points: List<Int>): Boolean {
for (i in points.indices) {
for (j in i + 1 until points.size) {
for (k in j + 1 until points.size) {
val sum3 = points[i] + points[j] + points[k]
if (sum3 % 10 == 0) {
val remainingPoints = points.filterIndexed { idx, _ -> idx != i && idx != j && idx != k }
val sum2 = remainingPoints.sum()
if (sum2 % 10 == 0) {
return true
}
}
}
}
}
return false
}
/**
* 检查手牌的牛型
* 按优先级从高到低判断:五小牛 > 五花牛 > 牛牛 > 普通牛 > 无牛
*/
fun checkNiuNiu(hand: List<String>): String {
val points = hand.map { cardToPoint(it) }
if (isFiveLittleCow(points)) {
return "五小牛"
}
if (isFiveFlowerCow(points)) {
return "五花牛"
}
if (isNiuNiu(points)) {
return "牛牛"
}
// 检查普通牛
for (i in hand.indices) {
for (j in i + 1 until hand.size) {
for (k in j + 1 until hand.size) {
val sum3 = points[i] + points[j] + points[k]
if (sum3 % 10 == 0) {
val remainingPoints = points.filterIndexed { idx, _ -> idx != i && idx != j && idx != k }
val sum2 = remainingPoints.sum()
val niuValue = sum2 % 10
return if (niuValue == 0) {
"牛十"
} else {
"牛$niuValue"
}
}
}
}
}
return "无牛"
}
/*** 将牛型转换为对应分值,用于比较大小 */
fun getNiuValue(niuStr: String): Int {
return when (niuStr) {
"五小牛" -> 13
"五花牛" -> 12
"牛牛" -> 11
"牛十", "牛10" -> 10
"牛九", "牛9" -> 9
"牛八", "牛8" -> 8
"牛七", "牛7" -> 7
"牛六", "牛6" -> 6
"牛五", "牛5" -> 5
"牛四", "牛4" -> 4
"牛三", "牛3" -> 3
"牛二", "牛2" -> 2
"牛一", "牛1" -> 1
else -> 0 // 无牛
}
}
/*** 判定庄家与闲家的胜负 */
fun judgeWinner(bankerNiu: String, playersNiu: List<String>): List<String> {
val results = mutableListOf<String>()
val bankerValue = getNiuValue(bankerNiu)
playersNiu.forEach { playerNiu ->
val playerValue = getNiuValue(playerNiu)
when {
playerValue > bankerValue -> results.add("赢")
playerValue < bankerValue -> results.add("输")
else -> results.add("平局 庄家赢")
}
}
return results
}
三、代码核心功能解析
该实现主要包含以下几个核心模块:
-
牌组生成 :通过
generateDeck()函数创建包含52张牌的完整牌组,使用花色与点数组合的方式生成牌面字符串。 -
点数转换 :
cardToPoint()函数将牌面转换为游戏中使用的点数,遵循斗牛游戏的点数计算规则。 -
牛型判定 :这是整个游戏最核心的部分,
checkNiuNiu()函数按照五小牛、五花牛、牛牛、普通牛的优先级依次判断手牌类型。 -
胜负判定 :通过
getNiuValue()将牛型转换为可比较的数值,再由judgeWinner()函数根据数值大小判定胜负。 -
游戏流程 :
main()函数实现了完整的游戏流程,包括洗牌、发牌、计算牛型和判定胜负,并将结果输出到控制台。
四、运行示例
运行程序后,会得到类似以下的输出结果:
庄家的手牌: ♠3 ♥5 ♦J ♣Q ♠10
闲家一手牌: ♥A ♦2 ♣3 ♠4 ♥5
闲家二手牌: ♦6 ♣7 ♠8 ♥9 ♦10
闲家三手牌: ♣J ♠Q ♥K ♦A ♣2
庄家: 五花牛
闲家1: 五小牛
闲家2: 牛十
闲家3: 牛3
判定结果:
闲家1 赢
闲家2 输
闲家3 输
五、总结
通过上述代码,我们实现了一个完整的斗牛游戏逻辑。这个实现包含了从牌组生成、发牌、牛等级计算到胜负判定的全流程。
该代码采用了Kotlin的函数式编程特性,如flatMap、filterIndexed等高阶函数,使代码更加简洁易读。同时,通过将不同功能封装到独立函数中,提高了代码的可维护性和可扩展性。
有空在搞一个图形化界面,嗨起来。