更新於 2024/11/13閱讀時間約 33 分鐘

系統開發-06優化遊戲狀態

    上一篇新增遊戲狀態的控制,這一篇預計加入遊戲回合數與玩家輸贏次數,但若是用上一篇的作法,會多存好幾個Session,控制上也比較分散,所以來優化一下作法,就是新增一個類別專門來儲存這些控制變數

    raw-image

    新增一個Game類別

    //**新增**
    public class Game
    {
    public int totalRound { get; set; } //遊戲回合
    public int playerWin { get; set; } //玩家贏的局數
    public int dealerWin { get; set; } //玩家輸的局數
    public int tie { get; set; } //平局的局數
    public int status { get; set; } //遊戲狀態

    public Game()
    {
    totalRound = 1;
    playerWin = 0;
    dealerWin = 0;
    tie = 0;

    status = 0; //0-遊戲中,1-結束
    }
    }

    再調整原本的status的程式碼,並在牌局結束的地方統計回合與輸贏次數

    最後加做一個可隱藏牌堆的Checkbox,以下是完整的程式碼

    Default.aspx

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

    <!DOCTYPE html>

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <style>
    /*撲克牌圖片顯示在網頁上的尺寸大小*/
    img {
    width:100px;
    height:145px;
    }
    </style>
    </head>
    <body>
    <form id="form1" runat="server">
    <div>
    <table border="1" style="border-collapse:collapse">
    <tr>
    <!--新增 牌堆checkbox-->
    <td>牌堆<asp:CheckBox ID="CheckBox1" runat="server" AutoPostBack="true"/></td>
    <td>
    <asp:Label ID="lab_deck" runat="server" Text=""></asp:Label>
    </td>
    </tr>
    <!--新增:回合數-->
    <tr>
    <td colspan="2">
    <asp:Label ID="lab_round" runat="server" Text=""></asp:Label></td>
    </tr>
    <tr>
    <td>莊家</td>
    <td>
    <asp:Label ID="lab_dealer" runat="server" Text=""></asp:Label>
    </td>
    </tr>
    <tr>
    <td>玩家</td>
    <td>
    <asp:Label ID="lab_player" runat="server" Text=""></asp:Label>
    </td>
    </tr>
    <tr>
    <td colspan="2">
    <asp:Label ID="lab_msg" runat="server" Text=""></asp:Label>
    </td>
    </tr>
    <tr>
    <td colspan="2">
    <asp:Button ID="btn_hit" runat="server" Text="要牌(Hit)" OnClick="btn_hit_Click" />
    <asp:Button ID="btn_stand" runat="server" Text="停牌(Stand)" OnClick="btn_stand_Click" />
    <asp:Button ID="btn_newgame" runat="server" Text="新牌局" OnClick="btn_newgame_Click" />
    </td>
    </tr>
    </table>
    </div>
    </form>
    </body>
    </html>

    Default.aspx.cs

    public partial class _Default : System.Web.UI.Page
    {
    Deck deck; //排堆
    Player dealer; //莊家
    Player player; //玩家

    Game game;

    protected void Page_Load(object sender, EventArgs e)
    {
    //避免網頁載入後,每按一次按鈕都會更新(POST)
    //改為網頁載入更新就好
    if (IsPostBack == false)
    {
    //**新增**
    Session["Game"] = new Game();

    ResetGame();
    }

    UpdateUI();
    }

    protected void ResetGame() { //初始化遊戲參數與動作(初始發牌)
    //將資料存到Session
    Session["Deck"] = new Deck();
    Session["Dealer"] = new Player();
    Session["Player"] = new Player();

    //**新增**
    game = (Game)Session["Game"];
    game.status = 0;
    Session["Game"] = game;

    //初始發牌
    DrawForPlayer();
    DrawForDealer();
    DrawForPlayer();
    DrawForDealer();

    //清空label內容
    lab_deck.Text = "";
    lab_dealer.Text = "";
    lab_player.Text = "";
    lab_msg.Text = "";

    btn_hit.Enabled = true;
    btn_stand.Enabled = true;
    btn_newgame.Enabled = false;
    }

    protected void UpdateUI()
    {
    //**新增**
    game = (Game)Session["Game"];

    lab_round.Text = $"第{game.totalRound}回合 ( 玩家贏{game.playerWin}次,莊家贏{game.dealerWin}次,平手{game.tie}次)";

    deck = (Deck)Session["Deck"];

    //**新增**
    if (CheckBox1.Checked)
    {
    lab_deck.Text = deck.ShowCards();
    }
    else
    {
    lab_deck.Text = "";
    }

    //Label顯示前先清空內容
    lab_player.Text = "";
    lab_dealer.Text = "";

    player = (Player)Session["Player"];

    //顯示玩家手牌
    for (int i = 0; i < player.cards.Count; i++)
    {
    lab_player.Text += $"<img src='./images/{player.cards[i].Rank}-{player.cards[i].Suit}.png'>";
    }

    dealer = (Player)Session["Dealer"];

    //顯示莊家手牌
    for (int i = 0; i < dealer.cards.Count; i++)
    {
    //**新增**
    //加入status判斷,遊戲結束(status=1)不顯示蓋牌
    if (i == 0 && game.status == 0)
    {
    lab_dealer.Text += $"<img src='./images/BACK.png'>";
    }
    else
    {
    lab_dealer.Text += $"<img src='./images/{dealer.cards[i].Rank}-{dealer.cards[i].Suit}.png'>";
    }
    }
    }

    //發牌給莊家
    protected void DrawForDealer() {
    deck = (Deck)Session["Deck"];
    dealer = (Player)Session["Dealer"];

    Card c = deck.DrawCard();
    dealer.AddCard(c); //加到手牌

    Session["Dealer"] = dealer;
    Session["Deck"] = deck;
    }

    //發牌給玩家
    protected void DrawForPlayer() {
    deck = (Deck)Session["Deck"];
    player = (Player)Session["Player"];

    Card c = deck.DrawCard();
    player.AddCard(c); //加到手牌

    Session["Player"] = player;
    Session["Deck"] = deck;
    }

    //按鈕OnClick後觸發
    //玩家要牌,呼叫DrawForPlayer()
    protected void btn_hit_Click(object sender, EventArgs e)
    {
    DrawForPlayer();

    if (player.IsBusted())
    {
    lab_msg.Text = $"{player.CalculatePoints()}點,你爆掉了! 莊家贏!";

    //**新增**
    game.dealerWin += 1;
    game.status = 1;
    Session["Game"] = game;

    //牌局結束,不能再按發牌和停牌
    btn_hit.Enabled = false;
    btn_stand.Enabled = false;
    btn_newgame.Enabled = true;
    }

    UpdateUI();
    }

    //玩家停牌,換莊家要牌DrawForDealer(),直到計分比較輸贏
    protected void btn_stand_Click(object sender, EventArgs e)
    {
    player = (Player)Session["Player"];
    dealer = (Player)Session["Dealer"];

    //小於18點持續要牌
    while (dealer.CalculatePoints() < 18)
    {
    DrawForDealer();
    }

    //如果莊家沒有爆牌,比較莊家和玩家的點數
    int playerScore = player.CalculatePoints();
    int dealerScore = dealer.CalculatePoints();

    if (dealer.IsBusted() || playerScore > dealerScore)
    {
    //玩家贏
    if (dealer.IsBusted())
    {
    lab_msg.Text = $"莊家 {dealer.CalculatePoints()}點,玩家 {player.CalculatePoints()}點! 莊家爆掉了! 玩家贏!";
    }
    else
    {
    lab_msg.Text = $"莊家 {dealer.CalculatePoints()}點,玩家 {player.CalculatePoints()}點! 玩家贏!";
    }

    //**新增**
    game.playerWin += 1;
    }
    else if (playerScore < dealerScore)
    {
    //莊家贏
    lab_msg.Text = $"莊家 {dealer.CalculatePoints()}點,玩家 {player.CalculatePoints()}點! 莊家贏!";

    //**新增**
    game.dealerWin += 1;
    }
    else
    {
    //平手
    lab_msg.Text = $"莊家 {dealer.CalculatePoints()}點,玩家 {player.CalculatePoints()}點! 雙方平手!";

    //**新增**
    game.tie += 1;
    }

    //**新增**
    game.status = 1;
    Session["Game"] = game;

    btn_hit.Enabled = false;
    btn_stand.Enabled = false;
    btn_newgame.Enabled = true;

    UpdateUI();
    }

    //新牌局(下一回合),呼叫NextRound()
    protected void btn_newgame_Click(object sender, EventArgs e)
    {
    //ResetGame(); //已包含在NextRound()內
    //UpdateUI(); //已包含在NextRound()內

    NextRound(); //新回合
    }

    protected void NextRound() //下一回合,呼叫ResetGame()
    {
    game = (Game)Session["Game"];
    game.totalRound += 1;
    Session["Game"] = game;

    ResetGame();

    UpdateUI();
    }
    }

    public class Game
    {
    public int totalRound { get; set; } //遊戲回合
    public int playerWin { get; set; } //玩家贏的局數
    public int dealerWin { get; set; } //玩家輸的局數
    public int tie { get; set; } //平局的局數
    public int status { get; set; } //遊戲狀態

    public Game()
    {
    totalRound = 1;
    playerWin = 0;
    dealerWin = 0;
    tie = 0;

    status = 0; //0-遊戲中,1-結束
    }
    }

    public class Player
    {
    public List<Card> cards { get; private set; }

    public Player()
    {
    cards = new List<Card>();
    Init();
    }

    public void Init()
    {
    cards.Clear();
    }

    public void AddCard(Card card)
    {
    cards.Add(card);
    }

    //計算手牌分數
    public int CalculatePoints()
    {
    int points = 0;
    int aceCount = 0; //A拿到次數

    foreach (var card in cards)
    {
    points += card.Value;
    if (card.Rank == 1) aceCount++;
    }

    // 調整A的值,以避免爆牌
    while (points > 21 && aceCount > 0)
    {
    points -= 10;
    aceCount--;
    }

    return points;
    }

    //判斷是否爆牌
    public bool IsBusted()
    {
    return CalculatePoints() > 21;
    }
    }

    public class Deck
    {
    private Card[] cards; //牌堆(只能透過DrawCard取得,所以設定private)
    private int dealIndex = 0; //發牌順序
    string[] suitNames = { "黑桃", "紅心", "方塊", "梅花" };

    public Deck() { //建立類別
    cards = new Card[52];
    string[] suits = { "S", "H", "D", "C" };

    int index = 0;

    for (int i = 0; i < suits.Length; i++) //四種花色
    {
    for (int j = 1; j <= 13; j++) //一種花色13張牌1-13
    {
    cards[index] = new Card(suits[i], suitNames[i], j); //初始化
    index++;
    }
    }

    Init();
    }

    public void Init()
    {
    BubbleSort(cards); //復原順序
    Shuffle(cards); //重新洗牌
    dealIndex = 0; //回復預設值
    }

    //發牌
    public Card DrawCard()
    {
    Card card = null;

    if (dealIndex < cards.Length)
    {
    card = cards[dealIndex];
    }
    //超出一副牌的52張數量

    dealIndex += 1;
    return card; // 回傳抽到的牌
    }

    void Shuffle(Card[] cards) { //洗牌(生成時,自動洗牌,所以不需要設定public)
    Random rand = new Random();

    for (int i = 0; i < cards.Length; i++)
    {
    int j = rand.Next(cards.Length); //// 產生一個隨機索引 0-51

    //例如:
    // i=0, j=12,表示第一張牌 (cards[0] 黑桃1) 與第13張牌 (cards[12]黑桃13) 互換
    // 互換後的結果為,第一張牌 (cards[0] 黑桃13,第13張牌 (cards[12]黑桃1)
    // 一直換52次(每一個位置的牌都換一次)
    Swap(cards, i, j);
    }
    }

    //將撲克牌順序由小到大排序
    void BubbleSort(Card[] cards)
    {
    for (int i = 0; i < cards.Length; i++)
    {
    for (int j = 0; j < cards.Length - 1; j++) //反過來就是由大到小排序
    {
    if (cards[j].Seq > cards[j + 1].Seq)
    {
    Swap(cards, j, j + 1);
    }
    }
    }
    }

    //陣列中兩個元素交換,通過暫存變數的方式來實現交換
    void Swap(Card[] cards, int i, int j)
    {
    Card tmp = cards[i]; //將 cards[i] 的值暫存到 temp
    cards[i] = cards[j]; //將 cards[j] 的值賦給 cards[i]
    cards[j] = tmp; //將剛才暫存的 tmp 值賦給 cards[j]
    }

    public string ShowCards()
    {
    string str = string.Empty;

    for (int i = 0; i < cards.Length; i++)
    {
    if (i % 13 == 0) //13張牌排成一行
    {
    str += $"<br>{cards[i].ToString()}"; //換行
    }
    else
    {
    str += $",{cards[i].ToString()}"; //自訂顯示方式
    }
    }

    return str;
    }
    }

    public class Card
    {
    public string Suit { get; set; } // 花色
    public string SuitName { get; set; } // 花色名稱,為了顯示排堆時方便辨識
    public int Rank { get; set; } // 點數
    public int Seq { get; set; } // 順序

    public int Value
    { // 對應的數值
    get
    {
    if (Rank == 1) //A計算分數時先當作11點
    {
    return 11;
    }
    else if (Rank > 10)
    { //JQK計算分數都是10點
    return 10;
    }
    else
    {
    return Rank;
    }
    }
    }
    public Card(string suit, string SuitName, int rank) {
    this.Suit = suit;
    this.SuitName = SuitName;
    this.Rank = rank;
    } //建立類別

    // 顯示卡片的文字表示
    public override string ToString()
    {
    return $"{Rank} of {SuitName}";
    }
    }

    執行結果


    以上由C# ASP.NET開發的21點撲克牌遊戲,到此結束。

    未來也可以繼續延伸,例如用資料庫記錄遊戲狀態;或可以讓多個使用者遊玩並記錄;或是讓多人一起玩...等新增遊戲功能或變更架構,甚至開發遊戲平台串接多個遊戲。

    分享至
    成為作者繼續創作的動力吧!
    © 2024 vocus All rights reserved.