面向?qū)ο蟪绦蚓毩?xí)三——?dú)g樂比拼比牌小游戲準(zhǔn)備

接上上次的游戲選擇,我們來接著對其中一個(gè)小游戲(歡樂比拼)進(jìn)行實(shí)現(xiàn)

“歡樂比拼”比牌小游戲的游戲規(guī)則大致如下:

參與游戲的玩家 除當(dāng)前玩家外還有三個(gè)匹配的玩家,開局首先選擇桌號,對應(yīng)下不同的底注,選完桌號后每位玩家將隨機(jī)獲取一張牌,每位玩家根據(jù)自己牌面的大小可依次選擇操作(1.下注 2.跟注 3.比牌 4.棄牌 5.all-in),最終牌面大的玩家獲勝并贏得桌面金額,獲得游戲勝利。

今天我們主要實(shí)現(xiàn)游戲的前期準(zhǔn)備工作,話不多說,讓我們開始~

項(xiàng)目實(shí)訓(xùn)——游戲準(zhǔn)備(歡樂比拼)

玩家選擇完游戲后即將進(jìn)入小游戲開始玩,我們就假定玩家選擇的是歡樂比拼小游戲。下面來看看這個(gè)小游戲?qū)⑷绾伍_始吧。

一、程序分析

一個(gè)游戲的開始將進(jìn)行許多相關(guān)的準(zhǔn)備活動(dòng)。比如就玩家來說,需要匹配三名玩家,開始游戲時(shí)每位玩家要分發(fā)一張牌,以及與游戲規(guī)則相關(guān)的一系列參數(shù)初的始化等等。

(1)理清功能

這里我們就直接用東哥做好的思維導(dǎo)圖,今天要做的部分主要是從歡樂比拼一直到玩家選擇操作(展示不同選擇列表)為止。

歡樂比拼

根據(jù)游戲規(guī)則我們可知,當(dāng)當(dāng)前有玩家選擇了4.allin之后的玩家只有兩個(gè)選擇——4或五,因此要根據(jù)不同狀態(tài)判斷該顯示什么操作列表

(2)找對象->抽類

從游戲大廳進(jìn)入游戲——游戲大廳(GameCenter)、游戲的基類(IGame)
進(jìn)入歡樂比拼小游戲——?dú)g樂比拼(HappyPokerGame)
匹配游戲玩家——存放玩家基本屬性(Palyer)、實(shí)現(xiàn)對所有玩家的管理(PalyerManger)
給每位玩家發(fā)牌——存放牌的基本屬性(Poker、PokerSuit、PokerNumber)、實(shí)現(xiàn)對每張牌的管理(PokerManger)
將牌發(fā)給玩家——發(fā)牌操作(ExTools)
選擇桌號、操作——提示玩家進(jìn)行選擇,展示列表(Console)
存放列表等常量——常量類(Constants)

(3)理清各個(gè)類間要實(shí)現(xiàn)的具體功能

由于該部分涉及到的功能、類較多,只進(jìn)行主要簡單的敘述

首先,在游戲中心玩家進(jìn)行完選擇操作后,游戲中心要啟動(dòng)對應(yīng)的游戲?qū)ο?,為了?shí)現(xiàn)接承游戲?qū)ο?、避免直接應(yīng)用歡樂游戲類,因此需要一個(gè)所有小游戲的抽象夫類(IGame)在游戲中心中,其有一個(gè)抽象方法是游戲開始。

開始游戲就進(jìn)入到歡樂比拼(HappyPokerGame)中,先由當(dāng)前玩家選擇桌號(chooseTable),之后對每個(gè)玩家發(fā)牌(dealCards),在拓展工具類(ExTools)中實(shí)現(xiàn)該功能,準(zhǔn)備完之后即游戲正式開始(startGame)

游戲正式開始,根據(jù)玩家當(dāng)前處境顯示對應(yīng)操作列表(showAllInMenu or showNormalMenu)

(4)畫時(shí)序圖 / 畫類圖

注:該類圖只畫出本部分要實(shí)現(xiàn)的功能的方法,對于總程序來說不算完整


類圖

二、寫代碼

根據(jù)時(shí)序圖將各個(gè)類根據(jù)其實(shí)現(xiàn)的功能、屬性歸類,可分為以下幾個(gè)包方便管理

分包管理

1.poker包:Poker-PokerManger-PokerSuit-PokerNumber
Poker包程序截圖

PokerSuit和PokerNumber類:

package StudyDemo.poker

class PokerNumber(val number: String, val tag:Int) {
    override fun toString(): String {
        return number
    }
}
//////////////////////////////////////////////////////
package StudyDemo.poker

class PokerSuit(val suit:String,val tag:Int){
    override fun toString(): String {
        return suit
    }
}

Poker類

class Poker (val pokerNumber:PokerNumber,val pokerSuit:PokerSuit){
    override fun toString(): String {
        return "${pokerNumber}${pokerSuit}"
    }
}

PokerManger類:
因?yàn)樵谝痪钟螒蚪K始終都是對一副牌進(jìn)行操作,所以牌的初始化只需要進(jìn)行一次,此除使用了單例方法來實(shí)現(xiàn),將構(gòu)造函數(shù)私有化并通過伴生對象里的方法來實(shí)現(xiàn)初始化,因?yàn)槭菓屑虞d,創(chuàng)建時(shí)才加載且只初始化一次。
該類中的getSomePoker實(shí)現(xiàn)一副牌中對指定數(shù)量牌的抽取的功能,getOnePoker實(shí)現(xiàn)將牌分發(fā)給各個(gè)玩家

class PokerManger private constructor(){
    private val pokersList= arrayListOf<Poker>()//所有的撲克
    init {
        //創(chuàng)建一副牌
        for ((i,number) in POKERNUMBER_LISR.withIndex()){
            for ((j,suit) in POKERSUIT_LIST.withIndex()){
                pokersList.add(
                    Poker(PokerNumber(number,i), PokerSuit(suit,j))
                )
            }
        }
    }
    companion object{
        val sharedPokerManger:PokerManger by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
            PokerManger()
        }
    }
    //獲取幾張牌
    fun getSomePoker(count:Int):List<Poker>{
        val pokers= arrayListOf<Poker>()
        for (i in 1..count){
            pokers.add(getOnePoker())
        }
        return pokers
    }
    //    獲取一張撲克
    private fun getOnePoker(): Poker {
        val index= Random.nextInt(pokersList.size)
//    獲取隨機(jī)數(shù) 0-牌數(shù)
        val poker=pokersList[index]
//    刪除這張撲克
        pokersList.removeAt(index)
        return poker
    }

}

2.Player包:Player、PlayerManger
Player包工程截圖:

Player包

Player類:
根據(jù)游戲玩法,玩家所具有的基本屬性包括下面四個(gè),dadi法方實(shí)現(xiàn)的是選擇桌號扣除底牌的功能

class Player(val name: String, val id: Int){
    var money= DEFAULT_MONEY //金幣數(shù)
    var isLive:Boolean = ONLINE //存活狀態(tài)
    lateinit var poker:Poker //手上的牌
    var lastBet=0//上一次下注的金幣

//
    override fun toString(): String {
        val flag=if (isLive== ONLINE)"?" else "?"
        return "NO$id.$name-$money-$poker-$flag"
    }
    fun dadi(bet:Int){
        money-=bet
    }
}

PlayerManger類:
該處與PokerManger初始化類似,一局游戲終始終是一開始匹配的人,所以也使用單例方法實(shí)現(xiàn)只初始化一次。placeBottomBet方法實(shí)現(xiàn)對當(dāng)前卓上所有玩家下底注總和的記錄

class PalyerManger {
    private var palyersList= arrayListOf<Player>()//所有玩家
    init {
        //初始化玩家信息
        for ((i,name) in DEFAULT_PLAYER_NAME_LIAT.withIndex()){
            palyersList.add(Player(name,i+1))
        }
    }
    companion object{
//        單例對象
        val sharedManger:PalyerManger by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
            PalyerManger()
        }
    }
//    所有人打底,返回所有錢
    fun placeBottomBet(bet:Int):Int{
        for (player in palyersList){
            player.dadi(bet)
        }
        return palyersList.size*bet
    }
//獲取所有玩家信息
    fun getPlayerList()=palyersList
}

3.utils工具類包:Console、Constent、ExTools

工具包

ExTools類:實(shí)現(xiàn)發(fā)牌dealcards方法

package StudyDemo.utils

import StudyDemo.palyer.Player
import StudyDemo.poker.Poker

//頂層屬性 頂層方法 全局 靜態(tài)
fun dealPokersToPlayer(pokers:List<Poker>, players: ArrayList<Player>){
    for ((i,poker) in pokers.withIndex()){
        players[i].poker=poker
    }
}

Constants類中儲存本部分用到的參數(shù),包括一些列表的內(nèi)容(下注列表、兩種操作列表),getBoottomBet方法實(shí)現(xiàn)跟據(jù)玩家選擇將對應(yīng)的底注數(shù)目取出返回

const val DEFAULT_MONEY=1000
//提供固定玩家名字
val DEFAULT_PLAYER_NAME_LIAT= arrayOf("李四","張三","翠花","小王")


//撲克牌的點(diǎn)數(shù)與花色
val POKERNUMBER_LISR= arrayOf("2","3","4","5","6","7","8","9","10","J","Q","K","A")
val POKERSUIT_LIST= arrayOf("?","?","?","?")
//游戲桌列表
val TABLE_LIST= arrayOf("50","100","200","500")
fun getBoottomBet(index:Int):Int= TABLE_LIST[index].toInt()

//玩家操作列表 all-in菜單列表
val ALL_IN_BET_MENU= arrayOf("All-in","棄牌")
val NORMAL_BET_MENU= arrayOf("下注","跟注","棄牌","比牌","All-in")

Console類中本步用到的內(nèi)容:
展示所有玩家列表和當(dāng)前玩家列表,以及兩種操作菜單的顯示

fun getChoice():Int{
    while (true){
        "請選擇:".show()
        try {
            val choice=readLine()!!.toInt()
            if (choice in 1..currentMenuList.size){
                return choice
            }
        }catch (e:java.lang.Exception){
            "輸入不合法 ".show()
        }
    }
}

//顯示桌號列表
fun shoeTableMenu(){
    showLineStar()
    currentMenuList= TABLE_LIST
    showMenu(TABLE_LIST)
    showLineStar()
}

//展示玩家信息
fun showPlayerList(players:List<Player>){
    showLineStar()
    players.forEach{
        println(it)
    }
    showLineStar()
}
fun showCurrentPlayerInfo(palyer: Player){
    println("當(dāng)前玩家: $palyer")
}
//重要的東西不暴露


//顯示all-in操作列表
fun showAllInMenu(){
    showLineStar()
    currentMenuList= ALL_IN_BET_MENU
    showMenu(ALL_IN_BET_MENU)
    showLineStar()
}
//顯示正常列表
fun showNormalMenu(){
    showLineStar()
    currentMenuList= NORMAL_BET_MENU
    showMenu(NORMAL_BET_MENU)
    showLineStar()
}

4.game包:GameCenter、IGame、HappyPokerGame
GameCenter類:做選擇,進(jìn)入相應(yīng)游戲
基于上次練習(xí)二,需要增添的部分如下

fun chooseGame(){
        showGameMenu()
        getChoice()
        game=HappyPokerGame()
        game.start()
    }

IGame類:所有游戲的基類,聲明start()方法

abstract class IGame {
    abstract fun start()
}

最后就是來具體運(yùn)用這些方法的HappyPokerGame類:

class HappyPokerGame : IGame() {
    //   靈魂參數(shù)  注意“時(shí)機(jī)”
    private var allInStarIndex= INVALID_NUM //allin玩家數(shù)量
    private var lastbet = 0 //記錄上一位玩家下注金額
    private var palyerManger=PalyerManger.sharedManger //靜態(tài)單例,全局靜態(tài)
    private val pokerManger=PokerManger.sharedPokerManger
    private var tableMoney=0 //桌上的錢
    override fun start() {
         chooseTable()
         dealCards()
         startGame()
    }
//    選桌號,扣底注
    private fun chooseTable(){
//        顯示列表
        shoeTableMenu()
//        選擇對應(yīng)桌號的金幣
        "請選擇桌號".showWithEnter()
        val boottomBet= getBoottomBet(getChoice()-1)
//        所有人下注 記錄當(dāng)前下注金幣
        tableMoney=palyerManger.placeBottomBet(boottomBet)
        println("當(dāng)前金幣數(shù):$tableMoney")
    }
//    發(fā)牌
    fun dealCards(){
        dealPokersToPlayer(pokerManger.getSomePoker(4),
            palyerManger.getPlayerList()
        )
    }
//    開啟游戲
    fun startGame(){

    while (true){
        //    顯示玩家列表
        showPlayerList(palyerManger.getPlayerList())
        //顯示當(dāng)前玩家
        showCurrentPlayerInfo(palyerManger.getPlayerWithIndex(currentPalyerIndex))
        println("當(dāng)前金幣數(shù):$tableMoney")
        //顯示操作菜單前先判斷是不是all-in或者金幣不足
        if (isAllIn() || !palyerManger.isMoneyEnough(currentPalyerIndex,lastbet)){
            //顯示allin菜單
            showAllInMenu()
        }else{
            //顯示全菜單
            showNormalMenu()
        }
   }
//判斷是不是all-in
//    fun isAllIn()=allInStarIndex != INVALIDE_NUM
    fun isAllIn():Boolean{
        return allInStarIndex!= INVALID_NUM
    }
}

選擇完游戲后進(jìn)行桌號選擇、玩家匹配、發(fā)牌、根據(jù)玩家當(dāng)前狀態(tài)展示操作列表的運(yùn)行截圖:


該部分運(yùn)行截圖

三、總結(jié)

該部分實(shí)現(xiàn)的方法較多,相比起來類與類之間的關(guān)系就更復(fù)雜,代碼量也增加了不少,以面向?qū)ο蟮乃季S來寫程序,將功能都模塊化,在一定程度上大大簡潔化了一個(gè)多功能工程的實(shí)現(xiàn)。其中要注意的一些方面有:

特定的功能放在特定的包里,要盡量避免不該出現(xiàn)在此處的東西出現(xiàn)在了此處,如gamecenter類引入了游戲的基類IGame是合理的,而不是直接將HappyPokerGame類暴露于gamecenter里直接應(yīng)用;再比如對于poker列表與player列表,都以私有化保護(hù)起來,外部要獲取的時(shí)候是通過相應(yīng)的方法來調(diào)用,實(shí)現(xiàn)了保護(hù)。

作為該程序的第三部分就實(shí)現(xiàn)到這里啦,下次再見!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容