65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Microsoft Silverlight 的井字游戏

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (10投票s)

2014年5月30日

CPOL

4分钟阅读

viewsIcon

12588

downloadIcon

214

这篇技巧展示了我们如何使用Silverlight的酷炫功能在网页上制作井字棋游戏。

引言

我正在使用Microsoft Silverlight开发井字棋游戏。我将展示如何在Web环境中以编程方式开发一个交互式简单游戏,并以一种酷炫的方式扩展这个游戏。

Using the Code

我不会花时间去开发一个花哨的代码;但是,我强烈建议你们遵循代码规范,如果可以的话,编写更模块化的代码。

首先,你需要创建一个Silverlight项目,请记住,如果Visual Studio抱怨创建这样的项目,你可能安装了错误的版本。我遇到过这个问题,因为Visual Studio自动将我定向到32位版本,而我的机器是64位的。另外,我意识到,如果你安装了ReSharper 8.2.0.2160(可用于在Visual Studio中重构代码),它可能会与Silverlight冲突,你需要重新安装Silverlight。

当你的Silverlight应用程序准备就绪后,修改用户控件的XAML代码,即`MainPage.XAML`,如下所示

<Grid ShowGridLines="False" 
        x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="70" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="150" />
        </Grid.ColumnDefinitions>
</Grid>  

以上代码会在你的用户控件上创建网格,如图1所示。

图1. 为我们的用户控件设计的初始网格。

下一步是添加井字棋棋盘。我使用以下代码创建棋盘

<Grid ShowGridLines="False" x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="70" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="150" />
        </Grid.ColumnDefinitions>
        <Grid Grid.Row="1" Grid.Column="1" ShowGridLines="True" Background="BlanchedAlmond" Name="GridBoard">
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Button Width="30" Height="30" Content="" 
            Grid.Row="0" Grid.Column="0" Name="btn00"/>
            <Button Width="30" Height="30" Content="" 
            Grid.Row="0" Grid.Column="1" Name="btn01"/>
            <Button Width="30" Height="30" Content="" 
            Grid.Row="0" Grid.Column="2" Name="btn02"/>
            <Button Width="30" Height="30" Content="" 
            Grid.Row="2" Grid.Column="0" Name="btn20"/>
            <Button Width="30" Height="30" Content="" 
            Grid.Row="2" Grid.Column="1" Name="btn21"/>               
            <Button Width="30" Height="30" Content="" 
            Grid.Row="2" Grid.Column="2" Name="btn22"/>            
            <Button Width=" 30" Height="30" Content="" 
            Grid.Row="1" Grid.Column="0" Name="btn10"/>
            <Button Width=" 30" Height="30" Content="" 
            Grid.Row="1" Grid.Column="1" Name="btn11"/>
            <Button Width=" 30" Height="30" Content="" 
            Grid.Row="1" Grid.Column="2" Name="btn12"/>          
            </Grid>
</Grid> 

结果如图2所示。你可以更改任何Row/ColumnDefinition中的“`Width`”和“`Height`”属性来更改棋盘单元格的大小/灵活性。

图2. 游戏棋盘。

创建棋盘后,我在代码隐藏中添加一个方法,如下所示,并将此方法分配给棋盘上每个`Button`的`Click`事件。

private void btn_Click(object sender, RoutedEventArgs e)
        {
            if (game_ended)
                return;
            if ((sender as Button).Content != "X" && (sender as Button).Content != "O")
            {
                (sender as Button).Content = m_turn == 1 ? "X" : "O";
                int len = (sender as Button).Name.Length;
                int x = int.Parse((sender as Button).Name[len - 2].ToString());
                int y = int.Parse((sender as Button).Name[len - 1].ToString());
                if (m_board[x, y] == 0)
                {
                    m_board[x, y] = m_turn;
                    moveNo++;
                    if (CheckWinner(x, y))
                    {
                        MessageBox.Show(string.Format("The winner is Player {0}", m_turn));
                        game_ended = true;
                    }
                    else
                    {
                        if (moveNo == MaxMoveNo)
                        {
                            MessageBox.Show("Game result is a draw");
                            game_ended = true;
                        }
                        m_turn = (m_turn % 2) + 1;
                    }
                }
            }
        } 

在上述方法中,我有一个名为`game_ended`的全局变量,用于在游戏已结束时停止游戏。检查之后,我检查调用此方法的对象是否是一个标签不等于'`X`'和'`O`'的`Button`,这意味着此按钮以前没有被使用过。如果条件为`true`,我使用名为`m_turn`的全局变量检查玩家轮次,然后查找按钮在网格中的索引,请记住我们将按钮命名为`btn00`、`btn01`……`btn22`,所以我使用一个技巧来解析按钮的名称,以获取其末尾的两位数字,这可以关联按钮的坐标。在找到网格中被点击按钮的坐标后,我检查一个全局变量`m_board`(表示实际游戏,最初包含0),如果在由`x`和`y`给定的索引处的值之前没有被占用,则我将该元素的值更改为`m_turn`的值。`moveNo`是另一个全局变量,包含迄今为止的总移动次数,如果它超过等于棋盘元素数量的`MaxMoveNo`,则应该以平局结果结束游戏。稍后,我将为所有按钮分配此通用行为,因此我避免为每个按钮上的每个点击事件定义不同的方法。

游戏GUI如图3所示。

图3. 游戏示例。

我有一个如下所示的`CheckWinner`方法,用于检查当前玩家是否获胜

private bool CheckWinner(int x, int y)
        {
            int n = MaxRow;
            // check the row
            for (int i = 0; i < n; i++)
            {
                if (m_board[i, y] != m_turn)
                    break;
                if (i == n - 1)
                    return true; //already win
            }
            // check the col
            for (int i = 0; i < n; i++)
            {
                if (m_board[x, i] != m_turn)
                    break;
                if (i == n - 1)
                    return true; //already win
            }
            if (x == y)
            {
                // check the diag
                for (int i = 0; i < n; i++)
                {
                    if (m_board[i, i] != m_turn)
                        break;
                    if (i == n - 1)
                        return true; //already win
                }
                // check the anti diag
                for (int i = 0; i < n; i++)
                {
                    if (m_board[i, n - i - 1] != m_turn)
                        break;
                    if (i == n - 1)
                        return true; //already win
                }
            }
            return false;
        } 

`CheckWinner`方法检查所有可能的获胜方式,包括行、列和(反)对角线。用户2获胜的情况如图4所示。

图4. 获胜通知。

最后,我使用一些技巧来泛化更大/更小棋盘尺寸的游戏,使用一个名为`btnAddButton`的按钮,点击该按钮,我会隐藏棋盘并显示另一个网格,用户可以在其中定义新的棋盘尺寸。

GridBoard.Visibility = System.Windows.Visibility.Collapsed;
gridInput.Visibility = System.Windows.Visibility.Visible; 

`gridInput`定义如下

<Grid Grid.Column="2" Grid.Row="1" Height="239" 
HorizontalAlignment="Left" Margin="9,8,0,0" 
Name="gridInput" VerticalAlignment="Top" 
Width="139" Visibility="Visible" 
BindingValidationError="gridInput_BindingValidationError">
            <TextBox Height="23" HorizontalAlignment="Left" 
            Margin="52,20,0,0" Name="txtRows" 
            VerticalAlignment="Top" Width="58" 
            Text="{Binding Rows, Mode=TwoWay, ValidatesOnExceptions=True, 
            NotifyOnValidationError=True, UpdateSourceTrigger=Explicit}"/>
            <TextBox Height="23" HorizontalAlignment="Left" 
            Margin="52,57,0,0" Name="txtCols" 
            VerticalAlignment="Top" Width="58" />
            <TextBlock Height="23" HorizontalAlignment="Left" 
            Margin="16,24,0,0" Name="textBlock1" 
            Text="Rows" VerticalAlignment="Top" />
            <TextBlock Height="23" HorizontalAlignment="Left" 
            Margin="16,61,0,0" Name="textBlock2" 
            Text="Cols" VerticalAlignment="Top" />
            <Button Content="Set" Height="23" 
            HorizontalAlignment="Left" Margin="35,95,0,0" 
            Name="btnSet" VerticalAlignment="Top" 
            Width="75" Click="btnSet_Click" />
            <Button Content="Cancel" Height="23" 
            HorizontalAlignment="Left" Margin="35,136,0,0" 
            Name="btnCancel" VerticalAlignment="Top" 
            Width="75" Click="btnCancel_Click" />
</Grid> 

点击`btnSet`按钮,`btnSet_Click`将被调用,其中包含以下代码

 private void btnSet_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                txtRows.GetBindingExpression(TextBox.TextProperty).UpdateSource();
            }
            catch(Exception ex)
            {
                return;
            }
            if (_rows == 0)
                return;
            GridBoard.Children.Clear();
            GridBoard.RowDefinitions.Clear();
            GridBoard.ColumnDefinitions.Clear();
            MaxRow = _rows;//int.Parse(txtRows.Text);
            for (int i = 0; i < MaxRow; i++)
            {
                GridBoard.RowDefinitions.Add(new RowDefinition());
                GridBoard.ColumnDefinitions.Add(new ColumnDefinition());
            }
            for (int i = 0; i < MaxRow; i++)
                for (int j = 0; j < MaxRow; j++)
                {
                    Button btn = new Button();
                    btn.Width = 30;
                    btn.Height = 30;
                    btn.Content = "";
                    btn.SetValue(Grid.RowProperty, i);
                    btn.SetValue(Grid.ColumnProperty, j);
                    btn.Name = string.Format("btn{0}{1}", i, j);
                    btn.Click += btn_Click;
                    GridBoard.Children.Add(btn);
                }
            ResetBoard();
            gridInput.Visibility = System.Windows.Visibility.Collapsed;
            GridBoard.Visibility = System.Windows.Visibility.Visible;
        } 

当我创建5x5棋盘的情况如图5和6所示。

图5. 创建新的棋盘。

图6. 5x5棋盘。

首先,`btnSet_Click`方法检查在`txtRows`中输入的棋盘行数是否正确。然后`_rows`(这是一个全局变量,包含行数,并以前与它们相关联,这是一个控件,用于输入所需的`txtRows`)。

 public int Rows
        {
            get { return _rows; }
            set
            {
                if (value > 9 || value < 1)
                {
                    _rows = 0;
                    throw new Exception("Please enter a value between 1 - 11.");
                }
                _rows = value;
            }
        } 

在XAML端,我们需要添加

<TextBox Height="23" HorizontalAlignment="Left" Margin="52,20,0,0" 
        Name="txtRows" VerticalAlignment="Top" Width="58" 
        Text="{Binding Rows, Mode=TwoWay, ValidatesOnExceptions=True, 
        NotifyOnValidationError=True, UpdateSourceTrigger=Explicit}"/> 

尝试创建10x10棋盘时的错误信息如图7所示。

图7. 创建错误尺寸棋盘时的错误。

整个代码已附加。

祝你编程顺利。

© . All rights reserved.