C#-wpf一個簡單的井字棋程式
前言
好久沒有碰過程式碼了,上次看C#估計還是上個學期233,這次老師突然佈置了一個C#語言編寫的井字棋程式讓我有點措手不及.不過,仗著微軟爸爸的vs的強大的程式碼補全能力,最後還是將這個程式做好了(遇到了很多坑).
要求
該程式的要求還是有點多了,以下一一列舉:
1. 在程式初始化時,要求輸入玩家使用者名稱,輸入後出現提示文字,並選擇是先手還是後手.
2. 點選Fight!後,進入遊戲介面,在九宮格中下一步棋後,電腦(敵人)會判斷形勢並下第二部棋,如此直至有三子連成一線(玩家或電腦贏),或者格子被佔滿(和局).旁邊有提示按鈕,點選後會出現提示;得分按鈕會出現具體的得分介面;退出按鈕則會退出程式
3. 得分介面,點開後會顯示自己的得分情況.最高得分按鈕會記錄玩家玩這個遊戲以來所得的最高分;列印按鈕則會列印自己的具體得分情況
主體
首先製作開啟程式時出現的”登入”介面,即該程式的mainwindow:
樣式:
這裡要注意的是,只有當輸入完姓名後才能出現下面的提示文字等控制元件,所以應事先將其屬性定為隱藏,點選”commit”按鈕後觸發事件
private void btm_commit_click(object sender, RoutedEventArgs e)
{
if (TBox1.Text .Length>0&&TBox1.Text.Length<10)//判斷玩家輸入的文字長度,更為嚴謹的做法應該是判斷字母+數字的長度,以及玩家輸入的字串不符合時應出現提示資訊
{
storage.name = TBox1.Text;//定義在storage類中的靜態變數,為了儲存玩家名稱,成績等資訊
TBlock2.Text = "Hi " + storage.name + ". Welcome to the battlefield!Now,lets begin the game!!!Hey,do you want to place a bomb first?" ;//其實應該直接在前臺程式碼中寫好內容,不必在後臺再賦值,因多次修改程式碼所致
TBlock2.Visibility = Visibility.Visible;//顯示提示文字塊
RB1.Visibility = Visibility.Visible;//顯示單選按鈕,讓使用者選擇是否先手
RB2.Visibility = Visibility.Visible;//同上
btm_fight.Visibility = Visibility.Visible;//顯示進入遊戲介面的按鈕
btm_commit.IsEnabled = false;//禁止玩家再次點選commit按鈕
}
}
在玩家確定好是否先手後,就該點選Fight!按鈕進入遊戲介面了
private void to_main_page_click(object sender, RoutedEventArgs e)
{
if(RB1.IsChecked==true)//玩家點選"yes"按鈕時,設定靜態布林型別變數RButton的值並進入遊戲介面"mainpage"
{
RButton = true;
this.Content = new mainpage();
}
else if (RB2.IsChecked == true)//玩家點選"no"按鈕後,類同yes
{
RButton = false;
this.Content = new mainpage();
}
else//玩家沒有點選單選按鈕的情形,出現提示資訊並不觸發進入遊戲介面的事件,其實想想是否讓程式預先設定好"yes"單選按鈕就沒有這一步了呢?
{
TBcheck.Visibility = Visibility.Visible;
}
}
其次,開始製作井字棋程式最複雜的遊戲部分,即mainpage
樣式:
在拖好控制元件後,自然要去實現他們的功能.首先是九宮格,我的想法是玩家(假設是玩家先手,並永遠下”X”棋)下出第一步棋後,按鈕打印出”X”字母,並且在後臺的對應於該按鈕的值變為1;而電腦(永遠下”O”棋)下棋後,按鈕打印出”O”字母,並且在後臺的對應於該按鈕的值變為-1;而沒有下任何棋的按鈕則不會打印出任何字母,並且在後臺的對應於該按鈕的值為0.
private void Btm0_Click(object sender, RoutedEventArgs e)//按鈕點選觸發事件,即玩家點選後的結果.9個按鈕均與此類似
{
if (btm[btm0]==0)//按鈕與後臺的值組成鍵值對的字典,判斷當按鈕後臺的值為0時
{
btm0.Content="X";//按鈕btm0的內容變為"X",打印出"X"字母
btm[btm0] = 1;//按鈕後臺的值變為1
computer();//電腦的操作,事實上除了下另一步棋外,還執行了判斷是否贏或輸或平局的函式,因此沒有單獨列出
}
}
三字連成一線的情況總共有8種,為了更快速的判斷是否出現了特殊情況(兩子一線,三子一線),可以將這些情況列成一個8行3列的陣列以供判斷函式或者電腦使用
private void update()
{
array_for_judge[0] = new int[3] { btm[btm0], btm[btm1], btm[btm2] };//第一行3子
array_for_judge[1] = new int[3] { btm[btm3], btm[btm4], btm[btm5] };//第二行3子
array_for_judge[2] = new int[3] { btm[btm6], btm[btm7], btm[btm8] };//第三行3子
array_for_judge[3] = new int[3] { btm[btm0], btm[btm3], btm[btm6] };//第一列3子
array_for_judge[4] = new int[3] { btm[btm1], btm[btm4], btm[btm7] };//第二列3子
array_for_judge[5] = new int[3] { btm[btm2], btm[btm5], btm[btm8] };//第三列3子
array_for_judge[6] = new int[3] { btm[btm0], btm[btm4], btm[btm8] };//右斜3子
array_for_judge[7] = new int[3] { btm[btm2], btm[btm4], btm[btm6] };//左斜3子
}
每次玩家或者電腦走完一步棋後,都得判斷是否出現贏或輸或平局的情況,若出現,將重新整理棋盤並記錄資訊
private void judge()//判斷函式
{
update();//更新資訊
foreach (int[] tmp in array_for_judge)
{
if (Array.TrueForAll(tmp, isone => (isone == 1)) == true)//判斷3子全為1,即玩家連成3子的情況
{
MessageBox.Show("you win!");//彈出訊息框,玩家贏
storage.battle++;//storage類的靜態變數,儲存對局次數
storage.win++;//同,儲存玩家贏的次數
flush();//重新整理棋盤,即設定按鈕文字全為空,後臺值全為0,在此不單獨列出該函式
}
else if (Array.TrueForAll(tmp, isone => (isone == -1)) == true)//判斷3子全為-1,即電腦連成3子的情況
{
MessageBox.Show("you lose!");
storage.battle++;
storage.lose++;
flush();
}
}
if (btm.ContainsValue(0) == false)//最後判斷沒有子為0,即棋盤下滿仍沒有勝負的情況,平局
{
MessageBox.Show("draw!");
storage.battle++;
storage.tie++;
flush();
}
}
電腦也需要根據棋盤的形勢下棋,在此列出3種情況:
1. 當電腦的棋子已有2子一線,並且該線無第3子時,下第3子取得勝利
2. 當玩家的棋子已有2子一線,並且該線無第3子時,下第3子阻止玩家取得勝利
3. 當無以上情況時,隨機選擇並落子
private int computer()//返回值是為了跳出該函式用的,並無太大意義
{
judge();//首先為玩家判斷是否已出現能贏或平局的可能
if (btm.ContainsValue(1) == false&&btm.ContainsValue(-1)==false)//判斷按鈕的值全為0的情形,即棋盤已被重新整理,跳出該函式
{
return 0;
}
for (int i = 0; i < array_for_judge.Length; i++)//進行特殊情況判斷
{
if (array_for_judge[i].Sum() == -2)//判斷2子為-1,另1子為0的情況,即第一種情況
{
int j = Array.IndexOf(array_for_judge[i], 0);
btm[to_dic_int(i, j)] = -1;//這裡我不知道如何從已列出特殊情形的陣列中取得原本按鈕的資訊,只好採取最笨拙的方式--定義函式建立特殊情形陣列與按鈕的對映,設定按鈕的值為-1
to_dic_int(i, j).Content()="O";//設定按鈕的內容為"O"
judge();//再次進行勝負判斷
return 0;
}
if (array_for_judge[i].Sum() == 2)//阻止玩家獲勝的情形
{
int j = Array.IndexOf(array_for_judge[i], 0);
btm[to_dic_int(i, j)] = -1;
to_dic_int(i, j).Background = storage.getImage2();
judge();
return 1;
}
}
Random random = new Random();//隨機落子的情形
while (true)無限迴圈直至隨機落子並且該按鈕的值為0
{
int tmp = random.Next(24);
int itmp = tmp % 8;
int jtmp = tmp % 3;
if (array_for_judge[itmp][jtmp] == 0)
{
btm[to_dic_int(itmp, jtmp)] = -1;
to_dic_int(itmp, jtmp).Background = storage.getImage2();
judge();
return 2;
}
}
}
還要設定電腦先手的情況
if (MainWindow.RButton == false)//在遊戲介面初始化時進行判斷,當玩家之前選擇false單選按鈕時讓電腦先執行一步
{
first_computer();//隨機讓電腦在九宮格中落子
}
Show Help按鈕
private void btm_tips_click(object sender, RoutedEventArgs e)
{
if (text_tips.Text == "")//事先設定好tips文字塊,不過內容為空
{
text_tips.Text = "Play The Battle of Waterloo! Start by placing bombs. The enemy will situate a bomb after you.";//顯示提示文字
btm_tips.Content = "Hide Help";//更改Show Help按鈕的內容
}
else//顯示文字塊內容後,再次點選按鈕
{
text_tips.Text = "";
btm_tips.Content = "Show Help";
}
}
Show Score按鈕
private void main_to_score(object sender, RoutedEventArgs e)
{
score_page score = new score_page();//建立score視窗物件
score.Show();//顯示score視窗
}
Exit按鈕
private void Exit_click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();//退出程式
}
最後,製作井字棋的得分介面,即score_page
拖幾個文字塊的事情…值得注意的是,右邊的分數需事先讀取玩家資料並將其顯示出來
Score_player.Text = storage.name;
Score_battles.Text = storage.battle.ToString();
Score_win.Text = storage.win.ToString();
Score_lose.Text = storage.lose.ToString();
Score_tie.Text = storage.tie.ToString();
Top Score和Print按鈕的功能我還沒有實現.Top Score應該是事先讀取儲存檔案中的最高分的資料並用文字塊顯示出來;而Print則應彈出檔案資源管理器讓玩家選擇儲存資料的位置,然後將其儲存
總結
這個程式我搞了很長時間才讓其具備初始的功能,仍然有許多不完善的地方,比如介面的設計啦,數值的繫結啦等等.不過在這次編寫C#程式後可能我的下一次編寫就又不知道是什麼時候了233,因此我希望在這幾天完善完善比較好.
其實老師的要求是埋下地雷而不是井字棋,遊戲的名稱叫做”滑鐵盧戰役”,實際上和井字棋一摸一樣-.-,附上改”X”“O”為地雷圖片的圖: