C#-wpf一个简单的井字棋程序

来源:互联网 发布:网络催生新职业 编辑:程序博客网 时间:2024/06/04 17:59

前言

好久没有碰过代码了,上次看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”为地雷图片的图:
这里写图片描述

原创粉丝点击