Tic-Tac-Toe Using XNA in Windows Phone 7

XNA is basically the foundation for game development on the XBox, PC and now Windows Phone 7. In this article I am creating a Tic-Tac-Toe game for Windows Phone 7.


XNA is basically the foundation for game development for the XBox, PC and now Windows Phone 7. XNA currently encompasses Microsoft's entire Game Development Sections, including the standard Xbox Development Kit and XNA Game Studio, it is powering 2D and 3D gaming on the Zune HD. That limitation is a result of the implemented touch-screen and accelerometer inputs that replaced the mouse and keyboard inputs. To port this back to Windows, the input handlers and screen setup would need to be rewritten. 


Let's look at some benefits of XNA:
  • More image formats supported
  • Can make game for both Xbox and Phone
  • XNA content pipeline makes handling large stores of content easier
  • More GPU acceleration built in
  • Graphics blend modes.
  • Support for 3D
  • Superior performance.
  • Superior shader support
  • Drawing with a "tint" easier
  • Built in game loop
  • XNA's polling.
  • Lighter memory footprint for bitmap based games
Microsoft has been meeting with hundreds of developers and ISVs over the past year to find out exactly what they want from a platform, and almost 70-percent prioritized "ease of use". 

    "The expertise and familiarity with our tools is not lost. If you are a .NET developer today your skills and much of your code will
    move forward. If you are Silverlight or XNA developer today you're gonna be really happy. New developers to the platform will
    find a cohesive, well designed API set with super productive tools." Charlie Kindel, Microsoft
 

You're going to learn how to create a simple Tic-Tac-Toe game playable with friends. Tic-Tac-Toe is a simple game in which two players take alternating turns placing characters on a three by three grid. The first player uses an O and the second player uses an X. To win the game, a player must arrange three of their characters into a row, column, or diagonal. 

Let's Start:
  • Open Visual Studio 2010 and click the New Project
  • Choose XNA Game Studio 4.0 Framework
  • Select the Windows Phone Game 4.0 project template from the right side
  • Give your project an appropriate name like "TicTacToe"
     
    Open new project

  • Add all the images in the Content project named TicTacToeContent

    Add Existing Items


    Add Images
     
  • Describe custom enumeration for your game:

             
    public enum Player { None, Player1, Player2 }
     
  • Now you need to load each image in fields inside of your game class. For that open the file Game1.cs and add the following fields:

            Texture2D gridTexture;
            Rectangle gridRectangle; 

            Texture2D Playagainbnutton;
            Rectangle Playagainbnuttonposition; 

            Texture2D Player1Piece;
            Texture2D Player2Piece;

            Texture2D Player1win;
            Texture2D Player2win;
            Texture2D noWin;

            Texture2D Player1turn;
            Texture2D Player2turn;
     
  • Add the instance variables that will track the game's state:

              
    bool winnableplayer;
             Player winnerplayer;
             Player currentplayer;
             Player[,] grid;
     
  • Drawing the Game interface:

            public Game1()
            {
                UI = new GraphicsDeviceManager(this);
                Content.RootDirectory = "Content";
     
                UI.PreferredBackBufferWidth = 480;
                UI.PreferredBackBufferHeight = 800;
     
                TargetElapsedTime = TimeSpan.FromTicks(333333);
            }


             Game Interface

            protected override void Draw(GameTime gameTime)
            {
                GraphicsDevice.Clear(Color.Black);
     
                SB.Begin();
     
                Drawgamegrid();
                Drawplayerpieces();
                Drawplayerstatus();
                DrawPlayagainbnutton();
     
                SB.End();
     
                base.Draw(gameTime);
            }
     
            private void Drawgamegrid()
            {
                SB.Draw(gridTexture, gridRectangle, Color.White);
            }
     
            private void Drawplayerpieces()
            {
                for (int i = 0; i < 3; i++)
                {
                    for (int j = 0; j < 3; j++)
                    {
                        if (grid[i, j] != Player.None)
                        {
                            Texture2D texture = grid[i, j] == Player.Player1 ? Player1Piece : Player2Piece;
                            Rectangle position = Griddesign(i, j, texture.Width, texture.Height);
                            SB.Draw(texture, position, Color.White);
                        }
                    }
                }
            }
     
            private void Drawplayerstatus()
            {
                Texture2D texture;
                if (winnerplayer != Player.None)
                {
                    texture = winnerplayer == Player.Player1 ? Player1win : Player2win;
                }
                else if (!winnableplayer)
                {
                    texture = noWin;
                }
               
    else
                {
                    texture = currentplayer == Player.Player1 ? Player1turn : Player2turn;
                }
     
                Rectangle position = new Rectangle(SB.GraphicsDevice.Viewport.Width / 2 - (texture.Width / 2), 15, texture.Width, texture.Height);
                SB.Draw(texture, position, Color.White);
            }

             Player1 Status

             Player2 Status
     
            private void DrawPlayagainbnutton()
            {
                if (winnerplayer != Player.None || !winnableplayer)
                {
                    SB.Draw(Playagainbnutton, Playagainbnuttonposition, Color.White);
                }
            }
     
            private Rectangle Griddesign(int column, int row, int width, int height)
            {
                int centerofX = SB.GraphicsDevice.Viewport.Width / 2;
                int centerofY = SB.GraphicsDevice.Viewport.Height / 2;
     
                int x = centerofX + ((column - 1) * 150) - (width / 2);
                int y = centerofY + ((row - 1) * 150) - (height / 2);
     
                return new Rectangle(x, y, width, height);
            }
     
  • Now we need to reinitialize the application whenever someone clicks the button, for that we add the new method in class underneath the Initialize method as follows:

  •       
     protected override void Initialize()
            {
                Playagain();
                base.Initialize();
            }
     
            private void Playagain()
            {
                currentplayer = Player.Player1;
                winnableplayer = true;
                winnerplayer = Player.None;
     
                grid = new Player[3, 3];
                for (int i = 0; i < 3; i++)
                {
                    for (int j = 0; j < 3; j++)
                    {
                        grid[i, j] = Player.None;
                    }
                }
            }
     
  • Instantiate the Texture2D and Rectangle objects that are assigned to the fields:

            gridTexture = Content.Load<Texture2D>("Game_Grid");
            gridRectangle = new Rectangle(0, 0, SB.GraphicsDevice.Viewport.Width, SB.GraphicsDevice.Viewport.Height);
     
            Player1Piece = Content.Load<Texture2D>("Game_Player1");
            Player2Piece = Content.Load<Texture2D>("Game_Player2");
     
            Playagainbnutton = Content.Load<Texture2D>("Game_Playagain");
            Playagainbnuttonposition = new Rectangle(SB.GraphicsDevice.Viewport.Width / 2 - (Playagainbnutton.Width / 2), SB.GraphicsDevice.Viewport.Height - 95, Playagainbnutton.Width,
           
    Playagainbnutton.Height);
     
            Player1win = Content.Load<Texture2D>("Game_Player1win");
            Player1win = Content.Load<Texture2D>("Game_Player2win");
            noWin = Content.Load<Texture2D>("Game_noWin");
     
            Player1turn = Content.Load<Texture2D>("Game_Player1turn");
            Player2turn = Content.Load<Texture2D>("Game_Player2turn");
     
  • For Handling the touches on the screen modify the Update method in your Game1.cs class:

            protected
    override void Update(GameTime gameTime)
            {
                if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                {

                    this.Exit();
               }
                 TouchCollection touchesmark = TouchPanel.GetState();
     
                if (!touching && touchesmark.Count > 0)
                {
                    touching = true;
                    TouchLocation touch = touchesmark.First();
     
                    GameTouch(touch);
                    Handleplayagainbutton(touch);
                }
                else if (touchesmark.Count == 0)
                {
                    touching = false;
                }
     
                base.Update(gameTime);
            }
     
  • Touch Events of the Game:

            private void GameTouch(TouchLocation touch)
            {
                if (winnerplayer == Player.None)
                {
                    for (int i = 0; i < 3; i++)
                    {
                        for (int j = 0; j < 3; j++)
                        {
                            Rectangle box = Griddesign(i, j, 150, 150);
                            if (grid[i, j] == Player.None && box.Contains((int)touch.Position.X, (int)touch.Position.Y))
                            {
     
                                grid[i, j] = currentplayer;
     
                                CheckForWinplayer(currentplayer);
                                CheckForWinnableplayer();
                                currentplayer = currentplayer == Player.Player1 ? Player.Player2 : Player.Player1;
                            }
                        }
                    }
                }
            }

            private void Handleplayagainbutton(TouchLocation touch)
            {
                if ((winnerplayer != Player.None || !winnableplayer)
                    && Playagainbnuttonposition.Contains((int)touch.Position.X, (int)touch.Position.Y))
                {
                    Playagain();
                }
            }
      
  • The Win Logic:

             private void CheckForWinplayer(Player player)
            {
                Func<Player, bool> checkWinner = b => b == player;
                if (
                    grid.GameRows(0).All(checkWinner) || grid.GameRows(1).All(checkWinner) || grid.GameRows(2).All(checkWinner) ||
                    grid.GameColumns(0).All(checkWinner) || grid.GameColumns(1).All(checkWinner) || grid.GameColumns(2).All(checkWinner) ||
                    grid.GameDiagonal(ArrayClass.DiagonalDirection.DownRight).All(checkWinner) || grid.GameDiagonal(ArrayClass.DiagonalDirection.DownLeft).All(checkWinner)
                    )
                {
                    winnerplayer = player;
                }
            }

             Chech for Win player
     
            private void CheckForWinnableplayer()
            {
                if (winnerplayer == Player.None)
                {
                    Func<Player, bool> checkNone = b => b == Player.None;
                    if (!grid.Total().Any(checkNone))
                    {
                        winnableplayer = false;
                    }
                }
            }

Now we have to create some extension methods for the modifiers like public and static which is in the class declaration. These methods returns the items according to its declaration for easy querying:
  • Row
  • Column
  • Diagonal
  • All
You have to create these methods in a different class. So, right-click your project in the Solution Explorer and click Add Items and Add New Class; give the class a name (I used ArrayClass.cs) and modify your class declaration so it looks like the following:

       
public static IEnumerable<T> GameRows<T>(this T[,] array, int row)
        {
            var column1 = array.GetLowerBound(1);
            var column2 = array.GetUpperBound(1);
 
            for (int i = column1; i <= column2; i++)
            {
                yield return array[row, i];
            }
        }
 
        public static IEnumerable<T> GameColumns<T>(this T[,] array, int column)
        {
            var row1 = array.GetLowerBound(0);
            var row2 = array.GetUpperBound(0);
 
            for (int i = row1; i <= row2; i++)
            {
                yield return array[i, column];
            }
        }
 
        public static IEnumerable<T> GameDiagonal<T>(this T[,] array,
                                                 DiagonalDirection direction)
        {
            var row1 = array.GetLowerBound(0);
            var row2 = array.GetUpperBound(0);
            var column1 = array.GetLowerBound(1);
            var column2 = array.GetUpperBound(1);
 
            for (int row = row1, column = column1;
                 row <= row2 && column <= column2;
                 row++, column++)
            {
                int realColumn = column;
                if (direction == DiagonalDirection.DownLeft)
                    realColumn = column2 - column1 - column;
 
                yield return array[row, realColumn];
            }
        }

        public enum
DiagonalDirection
        {
            DownRight,
            DownLeft
        }
 
        public static IEnumerable<T> Total<T>(this T[,] array)
        {
            var row1 = array.GetLowerBound(0);
            var row2 = array.GetUpperBound(0);
            var column1 = array.GetLowerBound(1);
            var column2 = array.GetUpperBound(1);
            for (int row = row1; row <= row2; row++)
            {
                for (int column = column1; column <= column2; column++)
                {
                    yield return array[row, column];
                }
            }
        }
 

Your Game is ready to play; you can compile and run your project by hitting F5 and start playing. I hope you enjoyed this article and look forward to read your comments. 

Thank you..