Grafikk
Pointing
Børre Stenseth
Moduler>Grafikk>Sjakkbrett

Sjakkbrett

Hva
Image1
Et enkelt eksempel på interaktiv grafikk

Vi ønsker å lage en enkel framstilling av et sjakkbrett. Brettet skal være interaktivt i den forstand at vi kan flytte brikker med vanlig mouse-drag. Det er ikke noen kontroll av at enkelte trekk er lovlige i den forstand at de følger spillereglene i sjakk. Det er heller ingen kontroll av at hvit og svart trekker annenhver gang. Hensikten er bare å vise litt enkel interaktiv grafikk.

Programmet er bygget opp slik at det er, som vanlig, en Form (FormMain) som tar seg av alle begivenheter, og som viser fram de to eneste kontrollene: En button (quit) og et panel som brukes til å framstille sjakkbrettet.

Selve sjakkbrettet er er pakket inn i en klasse som tar seg av all logikk og alle ressurser og uttegning av selve sjakkbrettet. Klassen Chessboard har to indre klasser:

  • Square, holder styr på hva som til enhver tid befinner seg i en rute
  • Pawns, har bare en statisk metode som plukker fram riktig bilde

FormMain

Klassen er laget slik at den ikke vet noe særlig om sjakkbrettet. Det eneste den gjør er å tegne opp bokstavene og siffrene som angir navn på sjakkrutene. Ansvaret for dette burde nok vært flyttet til klassen ChessBoard.

Det er FormMain som husker om vi holder på å trekke på en brikke, hvor den var sist osv. Denne logikken er imidlertid hel uavhengig av vilken brikke eller sjakkbrettets geometri.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace test1
{
    public partial class FormMain : Form
    {
        ChessBoard Board = null;
        // variables to control the moving piece
        Boolean b_pieceIsMoving = false;
        int LastX = -1;
        int LastY = -1;
        int StartX = -1;
        int StartY = -1;
        public FormMain()
        {
            InitializeComponent();
            // panelBoard is squares only
            Board = new ChessBoard(panelBoard.Size);
        }
        private void paintPanel(object sender, PaintEventArgs e)
        {
            // work on the panel with the chessboard
            Graphics g=panelBoard.CreateGraphics();
            Board.drawBoard(g);
            g.Dispose();
            // work on the frame(this) itself
            g = this.CreateGraphics();
            // produce name om rows and columns
            Size s = panelBoard.Size;
            int top = panelBoard.Top;
            int left = panelBoard.Left;
            int dx = s.Width/8;
            int dy = s.Height/8;
            // need a font with a color, bold and gray is ok
            Font f = new Font(this.Font, FontStyle.Bold);
            Brush graybrush=new SolidBrush(Color.Gray);
            //north and south
            for (int x = 0; x <8; x++)
            {
                Char ch=(char)(int)('a'+x);
                String str=new String(ch,1);
                g.DrawString(str, Font, graybrush, 
                             left+dx/3+x*dx,top-15);
                g.DrawString(str, Font, graybrush, 
                             left+dx/3+x*dx, top+s.Height +5);
            }
            // east and west
            for (int y = 8; y >0; y--)
            {
                String str=y.ToString();
                int vpos = top + s.Height+dy/2 - y * dy;
                g.DrawString(str, Font, graybrush, 
                             left - 10, vpos);
                g.DrawString(str, Font, graybrush, 
                             left + s.Width + 5, vpos);
            }
        }
        private void PanelMouseDown(object sender, MouseEventArgs e)
        {
            // left button to pick a pawn
            if (e.Button == MouseButtons.Left)
            {
                b_pieceIsMoving = Board.PickUpPawn(e.X, e.Y);
                if (b_pieceIsMoving)
                {
                    // Capture events: all coming events routed to panel, 
                    panelBoard.Capture = true;
                    LastX = e.X;
                    LastY = e.Y;
                    StartX = e.X;
                    StartY = e.Y;
                    // set move cursor 
                    panelBoard.Cursor = new Cursor("resources/mover.cur");
                }
            }
        }
        private void PanelMouseMove(object sender, MouseEventArgs e)
        {
            if (b_pieceIsMoving)
            {
                Graphics g = panelBoard.CreateGraphics(); 
                Board.MovePawn(e.X, e.Y,g);
                // remove/show a rubberband line to indicate move in progress
                ControlPaint.DrawReversibleLine(
                    panelBoard.PointToScreen(new Point(StartX, StartY)),
                    panelBoard.PointToScreen(new Point(LastX, LastY)), 
                    SystemColors.Control);
                ControlPaint.DrawReversibleLine(
                    panelBoard.PointToScreen(new Point(StartX, StartY)),
                    panelBoard.PointToScreen(new Point(e.X, e.Y)), 
                    SystemColors.Control);                
                LastX = e.X;
                LastY = e.Y;
            }
        }
        private void PanelMouseUp(object sender, MouseEventArgs e)
        {
            panelBoard.Capture=false; 
            if (b_pieceIsMoving)
            {
                // remove the rubberband line to indicate move in progress
                ControlPaint.DrawReversibleLine(
                    panelBoard.PointToScreen(new Point(StartX, StartY)),
                    panelBoard.PointToScreen(new Point(LastX, LastY)), 
                    SystemColors.Control);                
                Board.DropPawn(e.X, e.Y);
                Graphics g = panelBoard.CreateGraphics();
                Board.drawBoard(g);
                LastX = -1;
                LastY = -1;
            }
            b_pieceIsMoving = false;
            // set back cursor to hand
            panelBoard.Cursor = System.Windows.Forms.Cursors.Hand;
        }
        private void buttonQuit_MouseClick(object sender, MouseEventArgs e)
        {
            this.Dispose();
        }
    }
}

ChessBoard

Alt som har med selve sjakkbrettet er pakket inn her.

public class ChessBoard
{
    Square[,] S = new Square[8, 8];
    Square fromS = null;
    Square toS = null;
    public ChessBoard(Size limits)
    {
        // set up squares
        Size s = limits;
        int dx = s.Width / 8;
        int dy = s.Height / 8;
        Color dark = Color.FromArgb(181, 148, 115);
        Color lite = Color.FromArgb(214, 206, 181);
        Color c = Color.Black;
        for (int x = 0; x < 8; x++)
        {
            if (c == dark) c = lite; else c = dark;
            for (int y = 0; y < 8; y++)
            {
                Rectangle R = 
                    new Rectangle(x * dx, y * dy, dx, dy);
                if (c == dark) c = lite; else c = dark;
                S[x, y] = new Square(R, c,x,y);
            }
        }
        // init board
        initBoard();
    }
    public void initBoard()
    {
        // set up as seen from white
        // white
        S[0, 7].SetPiece(piece.w_rook);
        S[7, 7].SetPiece(piece.w_rook);
        S[6, 7].SetPiece(piece.w_knight);
        S[1, 7].SetPiece(piece.w_knight);
        S[5, 7].SetPiece(piece.w_bishop);
        S[2, 7].SetPiece(piece.w_bishop);
        S[3, 7].SetPiece(piece.w_queen);
        S[4, 7].SetPiece(piece.w_king);
        for (int ix = 0; ix < 8; ix++) 
            S[ix, 6].SetPiece(piece.w_pawn);
        // black
        S[0, 0].SetPiece(piece.b_rook);
        S[7, 0].SetPiece(piece.b_rook);
        S[6, 0].SetPiece(piece.b_knight);
        S[1, 0].SetPiece(piece.b_knight);
        S[5, 0].SetPiece(piece.b_bishop);
        S[2, 0].SetPiece(piece.b_bishop);
        S[3, 0].SetPiece(piece.b_queen);
        S[4, 0].SetPiece(piece.b_king);
        for (int ix = 0; ix < 8; ix++) 
            S[ix, 1].SetPiece(piece.b_pawn);
    }
    public void drawBoard(Graphics g)
    {
        for (int x = 0; x < 8; x++)
            for (int y = 0; y < 8; y++)
                S[x, y].DrawMe(g);
    }
    public Boolean PickUpPawn(int x, int y)
    {
        fromS = FindSquare(x, y);
        if ((fromS != null)&&(fromS.GetPiece()!=piece.no_piece))
            return true;
        return false;
    }
    public void MovePawn(int x, int y, Graphics g)
    {
        // .. if we want to do something here
        Square currentS = FindSquare(x, y);
    }
    public Boolean DropPawn(int x, int y)
    {
        toS = FindSquare(x, y);
        if (toS == null)
            return false;
        // do drop action, is it a legal move
        if (LegalMove(fromS, toS))
        {
            if (fromS != toS)
            {
                toS.SetPiece(fromS.GetPiece());
                fromS.SetPiece(piece.no_piece);
            }
            return true;
        }
        return false;
    }
    private Square FindSquare(int x, int y)
    {
        for (int i = 0; i < 8; i++)
            for (int j = 0; j < 8; j++)
                if (S[i, j].contains(x, y))
                    return S[i, j];
        return null;
    }
    private Boolean LegalMove(Square fromS, Square toS)
    {
        // basis version, make sure that the squares are ok
        if ((fromS == null)||(toS == null))
            return false;
        // we accept the move if target square is empty
        if (toS.GetPiece() == piece.no_piece)
            return true;
        // we also accept if target square contains an opponent
        if (( (fromS.GetPiece() < piece.b_king) && 
              (toS.GetPiece() >= piece.b_king))
        ||  ((fromS.GetPiece() > piece.b_king) && 
             (toS.GetPiece() <= piece.b_king)))
            return true;
        return false;
    }
}

piece er definert slik:

public enum piece { no_piece,
                    w_king, w_queen, w_bishop, 
                    w_knight, w_rook, w_pawn,
                    b_king, b_queen, b_bishop, 
                    b_knight, b_rook, b_pawn};

Square

public class Square : Object
{
    Rectangle R = new Rectangle();
    Color C = Color.Azure;
    int row = 0;
    int col = 0;
    int P = 0;
    Char verticalName = '0';
    Char horizontalName = '0';
    public Square(Rectangle r, Color c,int x,int y)
    {
        R = r;
        C = c;
        row = x;
        col = y;
        verticalName = (char)(int)('1' + row);
        horizontalName = (char)(int)('a' + col);
    }
    public void SetPiece(piece p)
    {
        P = (int)p;
    }
    public piece GetPiece()
    {
        return (piece)P;
    }
    public Point getPos(){
        return new Point(row,col);
    }
    public void DrawMe(Graphics g)
    {
        g.FillRectangle(new SolidBrush(C), R);
        g.DrawRectangle(new Pen(Color.Black), R);
        g.DrawImage(Pawns.GetImage(P), new Point(R.Left, R.Top));
    }
    public Boolean contains(int x,int y)
    {
        return R.Contains(x, y);
    }
    
    public Point GetCenter(){
        return new Point((int)(R.Left + R.Width / 2), 
                         (int)(R.Top + R.Height / 2));
    }
}

Pawns

public static class Pawns : Object
{
    public static Image GetImage(int p)
    {
        switch (p)
        {
            case (int)piece.w_king: 
                return test1.Properties.Resources.wKonge;
            case (int)piece.w_queen: 
                return test1.Properties.Resources.wDronning;
            case (int)piece.w_bishop: 
                return test1.Properties.Resources.wLoper;
            case (int)piece.w_knight: 
                return test1.Properties.Resources.wSpringer;
            case (int)piece.w_rook: 
                return test1.Properties.Resources.wTarn;
            case (int)piece.w_pawn: 
                return test1.Properties.Resources.wBonde;
            case (int)piece.b_king: 
                return test1.Properties.Resources.bKonge;
            case (int)piece.b_queen: 
                return test1.Properties.Resources.bDronning;
            case (int)piece.b_bishop: 
                return test1.Properties.Resources.bLoper;
            case (int)piece.b_knight: 
                return test1.Properties.Resources.bSpringer;
            case (int)piece.b_rook: 
                return test1.Properties.Resources.bTarn;
            case (int)piece.b_pawn: 
                return test1.Properties.Resources.bBonde; 
            default: return test1.Properties.Resources.no_piece; 
        }
    }
}
Referanser
Prosjekt:
https://svn.hiof.no/svn/psource/Csharpspikes/chessboard
Vedlikehold
B.Stenseth, august 2005
(Velkommen) Moduler>Grafikk>Sjakkbrett (Brikker)