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

更新 發佈閱讀 33 分鐘

上一篇新增遊戲狀態的控制,這一篇預計加入遊戲回合數與玩家輸贏次數,但若是用上一篇的作法,會多存好幾個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}";
}
}

執行結果

raw-image
raw-image


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

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

留言
avatar-img
留言分享你的想法!
avatar-img
Phyxsius
0會員
19內容數
FIRE
你可能也想看
Thumbnail
透過蝦皮分潤計畫,輕鬆賺取零用金!本文分享5-6月實測心得,包含數據流程、實際收入、平臺優點及注意事項,並推薦高分潤商品,教你如何運用空閒時間創造被動收入。
Thumbnail
透過蝦皮分潤計畫,輕鬆賺取零用金!本文分享5-6月實測心得,包含數據流程、實際收入、平臺優點及注意事項,並推薦高分潤商品,教你如何運用空閒時間創造被動收入。
Thumbnail
單身的人有些會養寵物,而我養植物。畢竟寵物離世會傷心,植物沒養好再接再厲就好了~(笑)
Thumbnail
單身的人有些會養寵物,而我養植物。畢竟寵物離世會傷心,植物沒養好再接再厲就好了~(笑)
Thumbnail
不知你有沒有過這種經驗?衛生紙只剩最後一包、洗衣精倒不出來,或電池突然沒電。這次一次補貨,從電池、衛生紙到洗衣精,還順便分享使用心得。更棒的是,搭配蝦皮分潤計畫,愛用品不僅自己用得安心,分享給朋友還能賺回饋。立即使用推薦碼 X5Q344E,輕鬆上手,隨時隨地賺取分潤!
Thumbnail
不知你有沒有過這種經驗?衛生紙只剩最後一包、洗衣精倒不出來,或電池突然沒電。這次一次補貨,從電池、衛生紙到洗衣精,還順便分享使用心得。更棒的是,搭配蝦皮分潤計畫,愛用品不僅自己用得安心,分享給朋友還能賺回饋。立即使用推薦碼 X5Q344E,輕鬆上手,隨時隨地賺取分潤!
Thumbnail
身為一個典型的社畜,上班時間被會議、進度、KPI 塞得滿滿,下班後只想要找一個能夠安靜喘口氣的小角落。對我來說,畫畫就是那個屬於自己的小樹洞。無論是胡亂塗鴉,還是慢慢描繪喜歡的插畫人物,那個專注在筆觸和色彩的過程,就像在幫心靈按摩一樣,讓緊繃的神經慢慢鬆開。
Thumbnail
身為一個典型的社畜,上班時間被會議、進度、KPI 塞得滿滿,下班後只想要找一個能夠安靜喘口氣的小角落。對我來說,畫畫就是那個屬於自己的小樹洞。無論是胡亂塗鴉,還是慢慢描繪喜歡的插畫人物,那個專注在筆觸和色彩的過程,就像在幫心靈按摩一樣,讓緊繃的神經慢慢鬆開。
Thumbnail
https://www.youtube.com/watch?v=0L2OgNQDzTc 之前看了啾啾鞋這部影片,裡面說退坑一款手遊,也說到一些觀念,就如下圖這樣
Thumbnail
https://www.youtube.com/watch?v=0L2OgNQDzTc 之前看了啾啾鞋這部影片,裡面說退坑一款手遊,也說到一些觀念,就如下圖這樣
Thumbnail
介紹一個優質的遊戲知識部落格:遊戲設計中藥鋪,其中「Game Design 資源分享表」十分推薦遊戲開發者閱讀。另外提到Gamker攻壳是一個專業的遊戲評鑑頻道,其深入的評論幫助作者入坑《健身環大冒險》。作者在後記也分享了自己在遊戲開發上的經歷和挑戰。
Thumbnail
介紹一個優質的遊戲知識部落格:遊戲設計中藥鋪,其中「Game Design 資源分享表」十分推薦遊戲開發者閱讀。另外提到Gamker攻壳是一個專業的遊戲評鑑頻道,其深入的評論幫助作者入坑《健身環大冒險》。作者在後記也分享了自己在遊戲開發上的經歷和挑戰。
Thumbnail
遊戲產業似乎從很久以前,各種玩法的類型都已經確立了下來。現在新出品的遊戲,更像是已有玩法機制的排列組合、重新包裝。比起遊戲本身,遊戲公司更願意把創意放在有明確衡量標準的部分,如:劇情、音樂、特效…等。久而久之,人們對遊戲的評價越來越取決於美術、故事等決定表現力的指標...
Thumbnail
遊戲產業似乎從很久以前,各種玩法的類型都已經確立了下來。現在新出品的遊戲,更像是已有玩法機制的排列組合、重新包裝。比起遊戲本身,遊戲公司更願意把創意放在有明確衡量標準的部分,如:劇情、音樂、特效…等。久而久之,人們對遊戲的評價越來越取決於美術、故事等決定表現力的指標...
Thumbnail
一、了解思維 二、大量體驗 三、拆解架構 四、找出樂趣
Thumbnail
一、了解思維 二、大量體驗 三、拆解架構 四、找出樂趣
Thumbnail
《獨立遊戲空間》Podcast提供了豐富的獨立遊戲相關主題內容,包括獨立遊戲專訪、開發誌、筆記、雜談以及GDC翻譯等等。對於喜愛獨立遊戲的玩家來說,這是一個值得收聽的Podcast。如果你是獨立遊戲愛好者或者是想開發遊戲的外行人,歡迎你來收聽並加入討論!
Thumbnail
《獨立遊戲空間》Podcast提供了豐富的獨立遊戲相關主題內容,包括獨立遊戲專訪、開發誌、筆記、雜談以及GDC翻譯等等。對於喜愛獨立遊戲的玩家來說,這是一個值得收聽的Podcast。如果你是獨立遊戲愛好者或者是想開發遊戲的外行人,歡迎你來收聽並加入討論!
Thumbnail
這篇文章討論了遊戲美術專案執行和技術面可能遇到的問題,包括人的問題以及技術困難,並提出瞭解決方法。
Thumbnail
這篇文章討論了遊戲美術專案執行和技術面可能遇到的問題,包括人的問題以及技術困難,並提出瞭解決方法。
Thumbnail
這篇文章分享筆者在 2019 年時協助趨勢科技運用遊戲化(Gamification)推動 DevOps 企業文化轉型活動的過程。 你正嘗試應用遊戲化方法嗎?筆者整理了自己的實務經驗,設計了一個「遊戲化設計畫布」工具,用來幫助自己在未來的遊戲化專案中找出設計時的盲點。歡迎來看看如何運用!
Thumbnail
這篇文章分享筆者在 2019 年時協助趨勢科技運用遊戲化(Gamification)推動 DevOps 企業文化轉型活動的過程。 你正嘗試應用遊戲化方法嗎?筆者整理了自己的實務經驗,設計了一個「遊戲化設計畫布」工具,用來幫助自己在未來的遊戲化專案中找出設計時的盲點。歡迎來看看如何運用!
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News