Blue Theme Orange Theme Green Theme Red Theme
 
Nevron Chart
Home | Forums | Videos | Advertise | Certifications | Downloads | Blogs | Interviews | Jobs | Beginners | Training
 | Consulting  
Submit an Article Submit a Blog 
 Jump to
Skip Navigation Links
TechnologyExpand Technology
WebsiteExpand Website
Team Foundation Server Hosting
Search :       Advanced Search »
Home » GDI+ & Graphics » Chess Knight Tour with C# and GDI+

Chess Knight Tour with C# and GDI+

In this article you will learn how to wrap the Graphics object to create a Board class to render and maintain the state.

Author Rank :
Page Views : 5331
Downloads : 202
Rating :
 Rate it
Level : Intermediate
   Print Read/Post comments Post a comment  Similar Articles  
   Email to a friend  Bookmark  Author's other articles  
Download Files:
Demo.zip
 
 
DevExpress Free UI Controls
Become a Sponsor
 Tag Cloud
 Latest Jobs
More ... 
 Latest Interview Questions
More ... 



The other day while working on one of the slow projects that have a lot of repetitive coding I did a couple of Google searches for programming challenges or things to keep me motivated. I came across David Bolton section at About.com and found an interesting challenge which lead to this article on the knights tour with C# and GDI+.

First I would like to point out that his is not the best solution, but one that works great, perform well, and lends itself to extensibility.

Board and Graphics Object

The way I think of the Graphic object is as a wrapper to the graphic card or display device. For our board class the Graphics device will be passed in from the form OnPaint event or Image when we just want to output the results to an image. In the case that the render method of our Board class doesn't get call we will still need to specify a ParentSize and Dimension of the board (8x8, 20x20, 24x24) to create a logical representation for board rows and ranks calculations.


pic1.gif

The board variables consist of our internal dimension of type Size under the System.Drawing namespace to keep track of board size and canvas size. The following variable contains a ration percentage used for calculating how big we want our board squares to be in relation to the ratio of the canvas and dimension. The following default Brushes are used for our rendering function. Lastly our rectangle list will contain a list of rectangles that have been visited.

private Size m_dimension = new Size(8, 8);    
private Size m_parentSize = new Size(800, 600);    
private float m_ratioPercentage = 0.8f;            
private Brush m_defaultDark = new SolidBrush(Color.FromArgb(205, 133, 63));
private Brush m_defaultLight = new SolidBrush(Color.FromArgb(222, 184, 135));
private Brush m_defaultHighlight = new SolidBrush(Color.FromArgb(150, 240, 255, 255));
private List<Rectangle> m_highlighted;

The constructor will take care of setting some properties and calling the method to calculate the size of each square. The constructor could be change to take in different parameter or raise a NotImplementedException in the case we don't want to handle a particular case.

public Board()
{
// Defaults
DefaultPen = new Pen(Color.Black);
SmoothingMode = SmoothingMode.AntiAlias;
TextRenderingHint = TextRenderingHint.AntiAlias;
ParentSize = new Size(800, 600);
XOffset = 10;
YOffset = 10;
Pieces = new List<Piece>(1);

// Init
m_highlighted = new List<Rectangle>(1);

CaculateSquareSize();
}

The next part is going to be all our methods and their purpose, starting with the CaculateSquareSize since it is called from our constructor. The function is a simple calculation of the ratio of our canvas and dimension and once we have that, we take a given percentage to be the size of our square.

private void CaculateSquareSize()
 {
SquareSize = (int)Math.Min(ParentSize.Height / Dimensions.Height * m_ratioPercentage, ParentSize.Width / Dimensions.Width * m_ratioPercentage);
 }


The Render function uses a Graphics object to perform all our rendering. From top to bottom much like the XNA skeleton we want to clear the form in this case to white. Set both smoothing mode and text rendering hint to our property values. Next declare a bool flag to turn on and off to color our chess board. Much of the rest of the code is self explanatory, we want to create a square from left to right and top to bottom, and we also want to fill that square depending on our bool flag. The next two functions will fill our already visited squares with a light blue with some transparency and render our pieces.

public virtual void Render(Graphics graphics)
        {
            graphics.Clear(Color.White);
            graphics.SmoothingMode = this.SmoothingMode;
            graphics.TextRenderingHint = this.TextRenderingHint;
 
            bool darkFlag = true;
            for (int h = 0; h < Dimensions.Height; h++)
            {
                for (int w = 0; w < Dimensions.Width; w++)
                {
                    // Create Rectangle
                    Rectangle rect = new Rectangle(w * SquareSize + XOffset,
                                                   h * SquareSize + YOffset,
                                                   SquareSize,
                                                   SquareSize);
                    // Fill Rectangle
                    graphics.FillRectangle(darkFlag ? m_defaultDark : m_defaultLight, rect);

                    // Draw Border
                    graphics.DrawRectangle(DefaultPen, rect);

                    // Reverse
                    darkFlag = !darkFlag;
                }

                // New Row Reverse
                darkFlag = !darkFlag;
            }

            // Other Logic
            foreach (Rectangle rect in m_highlighted)
                graphics.FillRectangle(m_defaultHighlight, rect);

            // Pieces
            RenderPieces(graphics);
        }

The RenderPieces function is a little more involved since it will need to iterate through each Piece and recreate a square object for their location. After we have a square for their position we want to render a point in the middle to indicate we have visited the particular square. Lastly we will want to map all the moves performed by our piece.

     private void RenderPieces(Graphics graphics)
        {
            foreach (Piece piece in Pieces)
            {
                int h = piece.Loc.Row * SquareSize + YOffset;
                int w = piece.Loc.Rank * SquareSize + XOffset;
                Rectangle rect = new Rectangle(w,
                                               h,
                                               SquareSize,
                                               SquareSize);
                Point point = new Point(rect.X + SquareSize / 4,
                                        rect.Y + SquareSize / 4);
 
                piece.Render(graphics, point);
 
                Point prevPoint = new Point(0, 0);
                Pen pen = new Pen(new SolidBrush(Color.Black));
                foreach (Location loc in piece.Moves)
                {
                    h = loc.Row * SquareSize + YOffset;
                    w = loc.Rank * SquareSize + XOffset;
                    rect = new Rectangle(w,
                                         h,
                                         SquareSize,
                                         SquareSize);
                   
                    graphics.FillRectangle(m_defaultHighlight, rect);
                    graphics.DrawRectangle(pen, new Rectangle(rect.X + SquareSize / 2 - 2,
                                                              rect.Y + SquareSize / 2 - 2,
                                                              4,
                                                              4));
                    graphics.FillRectangle(new SolidBrush(Color.Black), new Rectangle(rect.X +
SquareSize / 2 - 2,
                                                              rect.Y + SquareSize / 2 - 2,
                                                              4,
                                                              4));
 
                    point = new Point(rect.X + SquareSize / 2,
                                     rect.Y + SquareSize / 2);
                    if (prevPoint.X > 0)
                    {
                        graphics.DrawLine(pen, prevPoint, point);
                    }
                    prevPoint = point;
                }
            }
        }

The last functions of importance are our GetRow and GetRank because they will take a mouse location and get the square that was clicked.

     public int GetRow(Point location)
        {
            return (location.Y - YOffset) / SquareSize;
        }
 
     public int GetRank(Point location)
        {
            return (location.X - XOffset) / SquareSize;
  }


Other Objects

The Piece base implements IDisposable because we want to make sure we dispose of our resources if we use images.

pic2.gif

Algorithm Implementation

Strategy2 function will need to be called while there are unvisited squares. Starting from the top the last piece will always be the knight in this case because it will be the only piece in the board. We want to get available moves and get available moves for each result. Once we have two moves ahead we want to make sure we move to the one with the least amount of possible moves.

private void Strategy2()
        {
               Engine.Piece piece = m_board.Pieces.Last();
            try
            {
                // Available moves for the current piece
            
                List<Engine.Location> moves = piece.GetAvailableMoves(piece.Loc,
m_board.Dimensions);
                List<Engine.Location> locationsToRemove = new List<Engine.Location>(1);
                List<Engine.LocationEnhance> locationsEnhance = new List<Engine.LocationEnhance
(1);
 
                // First we want to filter out the existing pieces
                for (int x = 0; x < moves.Count; x++)
                {
                    Engine.Location loc = moves[x];

                    if (piece.Moves.Contains(loc))
                    {
                        locationsToRemove.Add(loc);
                        continue;
                    }

                    List<Engine.Location> availableMoves = piece.GetAvailableMoves(loc, m_board.Dimensions);
                    List<Engine.Location> filterMoves = new List<Engine.Location>(1);
                    foreach (Engine.Location nLoc in availableMoves)
                        if (!piece.Moves.Contains(nLoc))
                            filterMoves.Add(nLoc);
 
                    locationsEnhance.Add(new Demo.Engine.LocationEnhance()
                    {
                        Loc = loc,
                        MoveCount = filterMoves.Count
                    });
                }
 
                List<Engine.LocationEnhance> lenhance = locationsEnhance.OrderBy(c =>
c.MoveCount).ToList<Engine.LocationEnhance>();
                piece.Moves.Add(lenhance.First().Loc);
                piece.Loc = lenhance.First().Loc;
            }
            catch (Exception)
            {
                if (piece.Moves.Count == 64)
                {
                    m_timer.Stop();
                }
            }
        }

 Form Tips

I like setting the forms DoubleBuffered property to true to eliminate the flicker found when invalidating within a timer elapsed event.

this.DoubleBuffered = true;

The easiest way to make sure the tour is done is to loop while the loop collection doesn't match the dimension multiplication.

while (m_board.Pieces.First().Moves.Count < m_board.Dimensions.Width *
                                                        m_board.Dimensions.Height)
            {
                Strategy2();
            }

pic3.gif

pic4.gif

Comment Request!
Thank you for reading this post. Please post your feedback, question, or comments about this post Here.
Login to add your contents and source code to this article
 [Top] Rate this article
 
 About the author
 
Felipe Ramos
Developer for the last 7-8 years using .NET technologies.
Looking for C# Consulting?
C# Consulting is founded in 2002 by the founders of C# Corner. Unlike a traditional consulting company, our consultants are well-known experts in .NET and many of them are MVPs, authors, and trainers. We specialize in Microsoft .NET development and utilize Agile Development and Extreme Programming practices to provide fast pace quick turnaround results. Our software development model is a mix of Agile Development, traditional SDLC, and Waterfall models.
Click here to learn more about C# Consulting.
 
Introducing MaxV - one click. infinite control. Hyper-V Hosting from MaximumASP.
Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Dynamic PDF
ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
Discover the Top 5 .NET Memory Management Fundamentals
To write the best .NET code, you need to know exactly how the .NET framework really manages memory. Ricky Leeks presents the Top 5 fundamental facts of .NET memory management. Learn more.
Nevron Chart for .NET 2010.1 Now Available
The leading .NET charting control now features PDF, Flash and Silverlight export, visualization of large datasets and more. Deliver true charting functionality to your BI, Scorecard, Presentation or Scientific apps. Download evaluation now.
ASP.NET 4 Hosting
Get 2 Months Free of ASP.NET Hosting for Only $4.95/month! Receive FREE MS SQL and MySQL Databases Including ASP.NET 4/3.5, MVC 3.0, Silverlight 4, Windows 2008/IIS 7.0 Plus FREE IIS 7 Modules. Host UNLIMITED ASP.NET Web Sites – Click Here!
 
 Post a Feedback, Comment, or Question about this article
Subject:
Comment:
Nevron Chart
Become a Sponsor
 Comments
Good work by Mahesh On August 31, 2010
Nice article.
Reply | Email | Modify 
Re: Good work by Felipe On August 31, 2010
Thank you, I will keep trying to improve on both topic and content.
Reply | Email | Modify 
Good work by wizard On September 30, 2010
Nice artice.
Reply | Email | Modify 
Good work by ngoc On December 19, 2011
Nice article
Reply | Email | Modify 
Team Foundation Server Hosting
 © 2012  contents copyright of their authors. Rest everything copyright Mindcracker. All rights reserved.