Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Laser Guided Tic Tac Toe Game using Webcam For Vision

0.00/5 (No votes)
1 Dec 2009 1  
In this article, we will put together a program which will allow us to play Tic-Tac-Toe game against computer with laser light and webcam for vision.
Laser_Guided_Tic_Tac_Toe__source_code_files_

Introduction

Image processing and games programming may be the most technically challenging job that a programmer can have. The top level programs may require the best from both programmer and computer. Tic-tac-toe is a simple game that makes it easy to build algorithms for the computer to play its own move. In this article, a interactive webcam based Tic Tac Toe game is developed. Program in coded in C# .NET using AForge .NET Framework for certain image processing tasks and image acquisition.

Webcam in Application

The first step would be getting video feed from webcam within our program. We can use AForge .NET Framework for accomplishing this. Image acquisition is simple and pretty fast using AForge .NET Framework. Check out the code snippet below:

private void getCamList()
{
    try
    {
        videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
        comboBox1.Items.Clear();
        if (videoDevices.Count == 0)
            throw new ApplicationException();

        DeviceExist = true;
        foreach (FilterInfo device in videoDevices)
        {
            comboBox1.Items.Add(device.Name);
        }
        comboBox1.SelectedIndex = 0; //make default to first cam
    }
    catch (ApplicationException)
    {
        DeviceExist = false;
        comboBox1.Items.Add("No capture device on your system");
    }
}

//refresh button
private void rfsh_Click(object sender, EventArgs e)
{
    getCamList();
}

//toggle start and stop button
private void start_Click(object sender, EventArgs e)
{
    if (start.Text == "&Start")
    {
        if (DeviceExist)
        {
            videoSource = 
	    new VideoCaptureDevice(videoDevices[comboBox1.SelectedIndex].MonikerString);
            videoSource.NewFrame += new NewFrameEventHandler(video_NewFrame);
            CloseVideoSource();
            //videoSource.DesiredFrameRate = 10;
            videoSource.Start();
            label2.Text = "Device running...";
            start.Text = "&Stop";
        }
        else
        {
            label2.Text = "Error: No Device selected.";
        }
    }
    else
    {
        if (videoSource.IsRunning)
        {
            timer1.Enabled = false;
            CloseVideoSource();
            label2.Text = "Device stopped.";
            start.Text = "&Start";
        }
    }
}

//eventhandler if new frame is ready
private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
    Bitmap img = (Bitmap)eventArgs.Frame.Clone();
    // All the image processing is done here...
    pictureBox1.Image = img;
}

//close the device safely
private void CloseVideoSource()
{
    if (!(videoSource == null))
        if (videoSource.IsRunning)
        {
            videoSource.SignalToStop();
            videoSource = null;
        }
}

Think of video as a continuous stream of images, for real time image processing we need to process each and every image captured by the webcam. I'm getting an average of 7 fps within my application. More the fps, more smoothly your program will run.

Design and GUI

As you can see in the picture above, I've created a very basic design for Tic Tac Toe using GDI+ of C# .NET.

Laser Detection & Gameplay

The program searches for the brightest red blob in webcam's field of views. We can do so by using two of the filters defined in AForge .NET Framework.

  1. Color filter to filter out everything except red laser light.
  2. Blob Counter to retrieve the position of laser dot or red blob within image.
 ColorFiltering filter = new ColorFiltering(); // define a color filter
// Define the range of RGB to retain within a processed image
filter.Red = new IntRange(254, 255);
filter.Green = new IntRange(0, 240);
filter.Blue = new IntRange(0, 240);
Bitmap tmp = filter.Apply(image); // apply the color filter to image 
IFilter grayscale = new GrayscaleBT709();// define  grayscale filter
			// b'coz blobcounter supports grayscale 8 bpp indexed
 tmp = grayscale.Apply(tmp); // applying the filter
// locking the bitmap data
 BitmapData bitmapData = tmp.LockBits(new Rectangle(0, 0, image.Width, image.Height),
     ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); 
 blobCounter blobCounter = new BlobCounter(); // define an object of blobcounter class
 blobCounter.ProcessImage(bitmapData); // process the locked bitmap data to find blobs
 blobCounter.ObjectsOrder = ObjectsOrder.Size; // arrange the blob in increasing size
 Rectangle[] rects = blobCounter.GetObjectsRectangles(); //get the rectangle out a blob
 tmp.UnlockBits(bitmapData);
 tmp.Dispose();
 if (rects.Length != 0)
 {
    backpoint = GetCenter(rects[0]); // getcenter is a function to find
		// the center of any rectangle ( this is position of laser light)
    DrawLines(ref image, backpoint); // draw horizontal and vertical line on a
				// center point (see the picture above)
 } 

After laser has been detected, the program keeps track on movement of laser dot and once laser is turned off, it places the user's move within that grid. To understand this, imagine a tic tac toe board with nine different grids as shown in the image below:

The algorithm is designed so that it splits every frame from webcam into nine equal rectangles depicting nine different grids of tic tac toe game. User move is placed within grid in which laser light is turned off. If you don't have laser light or you can't visualize this program, then check out the video at YouTube. For code optimization, we can further add filters before playing any move like checking grid is empty and not occupied by cross or zero, we can also add some pause or sleep so that before program could confirm that there is no laser light within webcam view.

Algorithm For Tic Tac Toe

I tried to implement AI the best way I could, even with a long code. Algorithms are designed with humanistic approach to play tic tac tic. It tries to win the game at first, preventing the other player from winning, and then tries to make a move avoiding "traps" and putting two computer pieces in a row. If none of this is possible, a random move is made.

using System;

namespace WebcamTicTacToe
{
    class ZeroCross
    {
        public bool winner_comp = false; // whether winner is computer
        public bool winner_you = false;// whether winner is user
        public bool tie = false;// game goes in tie
        private int[] zeros = { 0, 0, 0, 0, 0 };// stores moved played by user
        private int[] crosses = { 0, 0, 0, 0, 0 };//stores moves played by computer
        private int countero;//count the zeros played
        private int counterx;//count crosses played
        public int MakeAMove(int MyMove)//This function is called to make
			//a move by computer and takes user move as argument
        {
            zeros[countero] = MyMove; // stores the user move in member function
            countero++;
            if (counterx == 0) // if there is no crosses played make a random move
            {
                int rand_move = RandomMove();
                crosses[counterx] = rand_move;
                counterx++;
                return rand_move;
            }
            else
            {
                // this will  try to win the game at first,
	       // preventing the other player to win, and then tries to
	       // make a move avoiding "traps" and putting 
                // two computer pieces in a row
                //If none of this is possible, a random move is made.
                int[,] Pattern = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 },
		{ 1, 4, 7 }, { 2, 5, 8 }, { 3, 6, 9 }, { 1, 5, 9 }, { 7, 5, 3 } };
                int Compmove = 0;
                int FinalMove = 0;
                int winning_Move = 0;
                for (int i = 0; i < 8; i++)
                {
                    int oCounter = 0;
                    int xCounter = 0;
                    int Incase = 0;
                    for (int j = 0; j < 3; j++)
                    {

                        if (CrossSeek(Pattern[i, j]))
                        {
                            xCounter++;
                        }
                        else if (ZeroSeek(Pattern[i, j]))
                        {
                            oCounter++;
                        }
                        else
                        {
                            Incase = Pattern[i, j];
                        }
                    }
                    if (oCounter == 3)
                    {
                        winner_you = true;
                        return 0;
                    }
                    else if (xCounter == 2 && Incase != 0)
                    {
                        winning_Move = Incase;
                    }
                    else if (oCounter == 2 && Incase != 0)
                    {
                        Compmove = Incase;
                    }
                    else if (oCounter == 0 && xCounter == 1)
                    {
                        if (Compmove == 0)
                        {
                            Compmove = Incase;
                        }
                    }
                    else if (Incase != 0)
                    {
                        FinalMove = Incase;
                    }

                }
                if (winning_Move != 0)
                {
                    crosses[counterx] = winning_Move;
                    counterx++;
                    winner_comp = true;
                    return winning_Move;
                }
                if (Compmove != 0)
                {
                    crosses[counterx] = Compmove;
                    counterx++;
                    if (counterx == 5 || countero == 5)
                    {
                        winner_you = false;
                        winner_comp = false;
                        tie = true;
                    }
                    return Compmove;
                }
                if (FinalMove != 0)
                {
                    crosses[counterx] = FinalMove;
                    counterx++;
                    if (counterx == 5 || countero == 5)
                    {
                        tie = true;
                    }
                    return FinalMove;
                }
                tie = true;
                return 0;
            }
        }
        // In case computer play first move
        public int CompFirstTurn()
        {
            int CompMove = RandomMove();
            crosses[counterx] = CompMove;
            counterx++;
            return CompMove;
        }
        // get winner is called at end to find the winner
        public bool GetWinner()
        {
            if (winner_comp || winner_you || tie)
            {
                return true;
            }
            return false;
        }
        // this searches whether any zero is placed at position passed in argument
        public bool ZeroSeek(int zero)
        {
            for (int i = 0; i < countero; i++)
            {
                if (zeros[i] == zero)
                {
                    return true;
                }
            }
            return false;
        }
        // this searches whether any cross is placed at position passed in argument
        public bool CrossSeek(int cross)
        {
            for (int i = 0; i < counterx; i++)
            {
                if (crosses[i] == cross)
                {
                    return true;
                }
            }
            return false;
        }
        //make a random move
        private int RandomMove()
        {
            Random random = new Random();
            int CompMove;
            bool truth = false;
            do
            {
                CompMove = random.Next(1, 10);
                if (zeros[0] == CompMove || crosses[0] == CompMove)
                {
                    truth = true;
                }
                else
                {
                    truth = false;
                }
            }
            while (truth);
            return CompMove;
        }
    }
} 

Using the Program

Well, the program needs a laser light and a webcam streaming images at 640 X 420, you can modify the source code provided in this article to match with your webcam's resolution.

I've added AI within the game for smooth gameplay even if your machine is not high end and it streams images at low frames per second. The program will try to predict the most probable move that you are uptop and will place it automatically even if your laser pointer is near it.

Conclusion

We have reached the end of this article. I might update this program to add more optimization for best game play. However, for now, have fun with it! You can find some videos of this application on my blog. You can also easily modify the code to make this program do much more than just playing tic tac toe. Have fun!

History

  • 1st December, 2009: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here