Lazarus

Programming => General => Topic started by: Soob on May 29, 2020, 09:15:48 pm

Title: Drawing a chessboard
Post by: Soob on May 29, 2020, 09:15:48 pm
Hi,
I realize there have probably been a lot of topics like that but I would like to ask you something. I underline that I don't need ready-written codes but I'd want to have some advice.

I would like to draw a simple 8x8 chessboard. I know it's probably an amateur-level task for many of y'all here but I always sucked when it comes to drawing in Pascal. I simply don't know how the drawing works. But drawing is the one problem cause even if I used canvas or TShape and drew the chessboard I don't know how to "communicate" with a particular squares, for example I would like to click on e4 square and the program should signal that e4 has been clicked.

Could someone try and explain to me where should I start with it? I would really appreciate that! :)
Title: Re: Drawing a chessboard
Post by: lucamar on May 29, 2020, 09:25:01 pm
A simple way would be to use one TShape per square (perhaps inside a TPanel to keep them together efforlestly), setting its Tag to some number, either consecutive 1..64 or as a mask of 6+6 bits refencing the column/row. Then, in a common OnClick event handler, you can interrogate the Tag to know which square was clicked. Shouldn't be too difficult ;)
Title: Re: Drawing a chessboard
Post by: TRon on May 29, 2020, 09:36:06 pm
A static chess-board ? i think i would go for a TDrawGrid https://lazarus-ccr.sourceforge.io/docs/lcl/grids/tdrawgrid.html
Title: Re: Drawing a chessboard
Post by: Handoko on May 29, 2020, 09:38:20 pm
@Soob

In game programming, you do not manipulate objects directly on the screen. Instead you create a virtual world, let's call it game world.

In most cases, you can use a 2 dimension array for the game world. Each position in the array (let's call it cell), can be occupied by an object. But for more complex games, each cell can have more than one object.

If you don't mind following a long boring tutorial, I recommend you to read the posts I wrote to explain a simple snake game:
https://forum.lazarus.freepascal.org/index.php/topic,38136.msg258381.html#msg258381 (https://forum.lazarus.freepascal.org/index.php/topic,38136.msg258381.html#msg258381)

In the snake game tutorial, each cell can be empty or occupied by a snake's body or a fruit. That's why:

Type
  TItem = (Empty, Snake, Fruit);

var
  GameWorld: array[1..WorldWidth, 1..WorldHeight] of TItem;

Note:
For beginner friendly reason, the array start from 1. But 'real' programmers count from 0.

Here is the most important thing when write a game:

Don't manipulate objects directly on the screen. Instead, you do it on the game world. Then write a procedure to draw the game world to the screen. When user moves the mouse or presses a button or key, process the user input based on the game world not the screen.

Have fun.

---edit---
Lucamar's suggestion is okay. It is beginner friendly, it starts simpler and easier but when you adds more features, it will be less (perfomance) efficient and harder to maintain.
Title: Re: Drawing a chessboard
Post by: Soob on May 29, 2020, 09:49:23 pm
Thank you guys for such a quick response. I'll think it through and see what I'll come up with.

I'm not a professional coder and I'll never be one and sometimes I find it hard when it comes to visualization of something. Even though I can make some more complex things, I still struggle with drawing etc 'cause it seems kind of unnatural for me.

I'm going to read the snake post now and see if it makes it more clear for me :)
thanks again!

btw. @Handoko, I know I can create an array of squares but I want it to appear on the screen. If I can create it only virtual, it won't appear on my screen and my biggest trouble so far here is to draw it correctly
Title: Re: Drawing a chessboard
Post by: Handoko on May 29, 2020, 09:56:51 pm
Try to understand the DrawGameWorld procedure in the code I provided in the link.
Title: Re: Drawing a chessboard
Post by: lucamar on May 30, 2020, 03:05:28 am
Frankly, for a chess game I wouldn't bother much with the drawing unless you want some "battle chess" kind of thing.

Unlike "action" games, even Handoko's relatively "simple" snake demo, in chess and alike it's much more important the "engine" (the part that plays chess or whatever) and drawing is (can be) rather simple (draw grid, draw pieces); you don't need extremely short drawing times to make the game responsive.

Even so, it's rather simpe to draw the board in a bitmap at the start of the program, and for each move copy the board to another bitmap, overlay the pieces, copy (assign) to, say, a TImage.Picture and get the position of a click (and calculate the square it's in) in the OnMouseDown/Up event. That's another way to do it, BTW  ;)
Title: Re: Drawing a chessboard
Post by: Fred vS on May 30, 2020, 04:44:57 am
Hello.

There are the excellent Chess Pascal engines of Roland Chastain:

https://github.com/rchastain/alouette

https://github.com/rchastain/durandal

https://github.com/rchastain/moustique


And his great UCI chess GUI developed in Pascal too:

https://github.com/rchastain/eschecs

Title: Re: Drawing a chessboard
Post by: Handoko on May 30, 2020, 06:09:46 am
I always sucked when it comes to drawing in Pascal. I simply don't know how the drawing works.

You can use TCanvas (on your mainform: Form1.Canvas). TCanvas is a bit slow but should be enough for simple games. One of the great thing about TCanvas is, it is relatively easy to use compare to the others.
https://wiki.lazarus.freepascal.org/TCanvas (https://wiki.lazarus.freepascal.org/TCanvas)


I don't know how to "communicate" with a particular squares, for example I would like to click on e4 square and the program should signal that e4 has been clicked.

These actually are 2 things you need to solve:
- Highlight the cell (signal that e4 has been clicked)
- Detect which cell the mouse is currently hovering

Highlighting the Cell

If you use my approach, this can be easily done. You just need to add ActiveX and ActiveY variables, and in the DrawGameWorld procedure, you draw a something on the cell[ActiveX, ActiveY]. Maybe a red rectangle to mark that the cell is selected.

Detecting Which Cell the Mouse is Currently Hovering

This one is a bit difficult if you are not good in math. You need to know on which PosX and PosY the cell start to draw, the CellSize and the MouseX, MouseY position.

For example:

PosX := 20; // Cell [0, 0] start to draw on this location
PosY := 30; // Cell [0, 0] start to draw on this location
CellSize := 40; // The width and height of a single cell
MouseX := 300;
MouseY := 200;

// The calculation becomes
HoveringX := (300 - 20) div CellSize
HoveringY := (200 - 30) div CellSize

If (HoveringX < 0) or (HoveringY < 0) or (HoveringX > MaxX) or (HoveringY > MaxY) then the mouse pointer is not hovering on any cell.
Title: Re: Drawing a chessboard
Post by: Thaddy on May 30, 2020, 09:54:24 am
Also note you can use a font to draw the pieces:
http://www.enpassant.dk/chess/fonteng.htm
Title: Re: Drawing a chessboard
Post by: Soob on May 31, 2020, 05:50:15 pm
thanks guys for recommendations but I don't want any pieces ;) It would be related to chess but no pieces are needed in my project ;)
Title: Re: Drawing a chessboard
Post by: Fred vS on May 31, 2020, 05:59:27 pm
It would be related to chess but no pieces are needed in my project ;)

Did you read the post about Chess Engines?

https://forum.lazarus.freepascal.org/index.php/topic,50016.msg364151.html#msg364151

Title: Re: Drawing a chessboard
Post by: Thaddy on May 31, 2020, 06:05:49 pm
I guess no, because those (and some more) are excellent.

 we gave him al that he needs to know:
- How to draw a board
- How to draw the pieces
- Examples of chess engines written in Pascal.

What does he need more?

(note chess players are usually good coders too, so I doubt his chess ratings. Anyway good questions get good answers.)
Title: Re: Drawing a chessboard
Post by: Fred vS on May 31, 2020, 06:11:08 pm
because those (and some more) are excellent.

And for me even too excellent.
I did not win once against  each of Roland engines.  :-X
Title: Re: Drawing a chessboard
Post by: Fred vS on May 31, 2020, 06:23:47 pm
Imho, very interesting too is the console application:

https://github.com/rchastain/eschecs-console

It makes play engine vs engine.
Title: Re: Drawing a chessboard
Post by: RAW on May 31, 2020, 08:16:35 pm
The easiest way I can think of ...  :)

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5.   Classes,SysUtils,Forms,Controls,Graphics,Dialogs;
  6. type
  7.   TBoardRec = Record
  8.    R:TRect;
  9.   End;
  10. type
  11.   TForm1 = class(TForm)
  12.    procedure FormCreate    (Sender:TObject);
  13.    procedure FormPaint     (Sender:TObject);
  14.    procedure FormMouseMove (Sender:TObject;Shift:TShiftState;X,Y:Integer);
  15.    procedure FormMouseDown (Sender:TObject;Button:TMouseButton;
  16.                             Shift:TShiftState; X, Y: Integer);
  17.    procedure GetDayRect    (iX,iY:Integer;Out iCX,iCY:Integer);
  18.   private
  19.    arrBoard: Array[1..8,1..8] Of TBoardRec;rBoard:TRect;iCell,iMX,iMY:Integer;
  20.   end;
  21. var
  22.   Form1: TForm1;
  23. implementation
  24. {$R *.lfm}
  25.  
  26. procedure TForm1.FormPaint(Sender:TObject);
  27. var
  28.  iy,ix,iCount,iSpaceX,iSpaceY:Integer;booColor:Boolean;
  29. begin
  30.   booColor:= True;
  31.   iSpaceX:= -iCell;
  32.   iSpaceY:= 0;
  33.   iCount:= 0;
  34.   // Drawing
  35.   For iy:= 1 To 8
  36.   Do
  37.    For ix:= 1 To 8
  38.    Do
  39.     Begin
  40.      Inc(iCount);
  41.       If iCount = 9
  42.       Then
  43.        Begin
  44.         iCount:= 1;
  45.         Inc(iSpaceY,iCell);
  46.         iSpaceX:= -iCell;
  47.         booColor:= Not booColor;
  48.        End;
  49.      Inc(iSpaceX,iCell);
  50.      // Set Color
  51.      If booColor
  52.      Then Canvas.Brush.Color:= clWhite
  53.      Else Canvas.Brush.Color:= clBlack;
  54.      // Fill RectArray
  55.      arrBoard[iy,ix].R:= Rect(ClientRect.Left+iSpaceX,
  56.       ClientRect.Top+iSpaceY,ClientRect.Left+iSpaceX+iCell,
  57.       ClientRect.Top+iSpaceY+iCell);
  58.      // Draw Cell
  59.      Canvas.FillRect(arrBoard[iy,ix].R);
  60.      booColor:= Not booColor;
  61.     End;
  62. end;
  63.  
  64. Function InsideRect(R:TRect;X,Y:Integer):Boolean;
  65. Begin
  66.   Result:= False;
  67.   If (X >= R.Left) And (Y <= R.Bottom) And (X <= R.Right) And (Y >= R.Top)
  68.   Then Result:= True;
  69. End;
  70.  
  71. procedure TForm1.FormMouseMove(Sender:TObject;Shift:TShiftState;X,Y:Integer);
  72. begin
  73.   // If You Need X,Y In OnPaint
  74.   iMX:= X;iMY:= Y;
  75.   Invalidate;
  76. end;
  77.  
  78. Procedure TForm1.GetDayRect(iX,iY:Integer;Out iCX,iCY:Integer);
  79. Var
  80.  d:Double;
  81. Begin
  82.   d:= (iY-rBoard.Top)/iCell;
  83.   iCY:= Trunc(d);
  84.   If Frac(d) <> 0 Then Inc(iCY);
  85.   If iCY > 8 Then iCY:= 8;
  86.   If iCY < 1 Then iCY:= 1;
  87.   d:= (iX-rBoard.Left)/iCell;
  88.   iCX:= Trunc(d);
  89.   If Frac(d) <> 0 Then Inc(iCX);
  90.   If iCX > 8 Then iCX:= 8;
  91.   If iCX < 1 Then iCX:= 1;
  92. End;
  93.  
  94. procedure TForm1.FormMouseDown
  95. (Sender:TObject;Button:TMouseButton;Shift:TShiftState;X,Y:Integer);
  96. var
  97.  iCX,iCY:Integer;
  98. begin
  99.   If InsideRect(rBoard,X,Y)
  100.   Then
  101.    Begin
  102.     GetDayRect(X,Y,iCX,iCY);
  103.     Caption:= 'Array Cell (iy, ix): '+IntToStr(iCY)+' '+IntToStr(iCX);
  104.    End;
  105. end;
  106.  
  107. procedure TForm1.FormCreate(Sender:TObject);
  108. begin
  109.   iCell:= 50;
  110.   rBoard:= Rect(0,0,400,400);
  111.   Caption:= 'Click Any Cell ...';
  112. end;
  113.  
  114. end.
Title: Re: Drawing a chessboard
Post by: RAW on June 01, 2020, 01:47:28 am
This is an example with an additional bitmap (DoubleBuffering) ...
- added Chess Notation ...  :)

Code: Pascal  [Select][+][-]
  1. UNIT Unit1;
  2. {$mode objfpc}{$H+}
  3. INTERFACE
  4. USES
  5.  Classes,SysUtils,Forms,Controls,Graphics;
  6. TYPE
  7.  TBoardRec = Record
  8.   R:TRect;
  9.  End;
  10. TYPE
  11.  TForm1 = class(TForm)
  12.   Procedure FormCreate    (Sender:TObject);
  13.   Procedure FormPaint     (Sender:TObject);
  14.   Procedure FormMouseMove (Sender:TObject;Shift:TShiftState;X,Y:Integer);
  15.   Procedure FormMouseDown (Sender:TObject;Button:TMouseButton;
  16.                            Shift:TShiftState;X,Y:Integer);
  17.   Procedure FormClose     (Sender:TObject;Var CloseAction:TCloseAction);
  18.   Procedure GetRect       (iX,iY:Integer;Out iCX,iCY:Integer);
  19.  PRIVATE
  20.   arrBoard: Array[1..8,1..8] Of TBoardRec;
  21.   rBoard:TRect;iCell,iMX,iMY:Integer;BMP:TBitmap; // DoubleBuffering ...
  22.  End;
  23. VAR
  24.  Form1: TForm1;
  25. IMPLEMENTATION
  26. {$R *.lfm}
  27.  
  28. Procedure TForm1.FormPaint(Sender:TObject);
  29. Var
  30.  iy,ix,iCount,iSpaceX,iSpaceY:Integer;booColor:Boolean;
  31. Begin
  32.   booColor:= True;
  33.   iSpaceX:= -iCell;
  34.   iSpaceY:= 0;
  35.   iCount:= 0;
  36.   For iy:= 1 To 8
  37.   Do
  38.    For ix:= 1 To 8
  39.    Do
  40.     Begin
  41.      Inc(iCount);
  42.       If iCount = 9
  43.       Then
  44.        Begin
  45.         iCount:= 1;
  46.         Inc(iSpaceY,iCell);
  47.         iSpaceX:= -iCell;
  48.         booColor:= Not booColor;
  49.        End;
  50.      Inc(iSpaceX,iCell);
  51.      // Set Color
  52.      If booColor
  53.      Then BMP.Canvas.Brush.Color:= clWhite
  54.      Else BMP.Canvas.Brush.Color:= clBlack;
  55.      // Fill RectArray
  56.      arrBoard[iy,ix].R:= Rect(ClientRect.Left+iSpaceX,
  57.       ClientRect.Top+iSpaceY,ClientRect.Left+iSpaceX+iCell,
  58.       ClientRect.Top+iSpaceY+iCell);
  59.      // Draw Cell
  60.      BMP.Canvas.FillRect(arrBoard[iy,ix].R);
  61.      booColor:= Not booColor;
  62.     End;
  63.   // Draw BMP Buffer On Form Canvas
  64.   Canvas.Draw(0,0,BMP);
  65. End;
  66.  
  67. Function InsideRect(R:TRect;X,Y:Integer):Boolean;
  68. Begin
  69.   Result:= False;
  70.   If (X >= R.Left) And (Y <= R.Bottom) And (X <= R.Right) And (Y >= R.Top)
  71.   Then Result:= True;
  72. End;
  73.  
  74. Procedure TForm1.FormMouseMove(Sender:TObject;Shift:TShiftState;X,Y:Integer);
  75. Begin
  76.   // If You Need X,Y In OnPaint
  77.   iMX:= X;iMY:= Y;
  78.   Invalidate;
  79. End;
  80.  
  81. Procedure TForm1.GetRect(iX,iY:Integer;Out iCX,iCY:Integer);
  82. Var
  83.  d:Double;
  84. Begin
  85.   d:= (iY-rBoard.Top)/iCell;
  86.   iCY:= Trunc(d);
  87.   If Frac(d) <> 0 Then Inc(iCY);
  88.   If iCY > 8 Then iCY:= 8;
  89.   If iCY < 1 Then iCY:= 1;
  90.   d:= (iX-rBoard.Left)/iCell;
  91.   iCX:= Trunc(d);
  92.   If Frac(d) <> 0 Then Inc(iCX);
  93.   If iCX > 8 Then iCX:= 8;
  94.   If iCX < 1 Then iCX:= 1;
  95. End;
  96.  
  97. Procedure TForm1.FormMouseDown
  98. (Sender:TObject;Button:TMouseButton;Shift:TShiftState;X,Y:Integer);
  99. Var
  100.  iCX,iCY:Integer;
  101.  arrAH: Array[1..8] Of String = ('a','b','c','d','e','f','g','h');
  102. Begin
  103.   If InsideRect(rBoard,X,Y)
  104.   Then
  105.    Begin
  106.     GetRect(X,Y,iCX,iCY);
  107.     Caption:= 'Array Cell (iy, ix): '+IntToStr(iCY)+' , '+IntToStr(iCX)+
  108.      '  Chess Notation: '+arrAH[iCX]+IntToStr(((8-iCY)+1));
  109.    End;
  110. End;
  111.  
  112. Procedure TForm1.FormCreate(Sender:TObject);
  113. Begin
  114.   iCell:= 50;
  115.   rBoard:= Rect(ClientRect.Left,ClientRect.Top,
  116.    ClientRect.Left+400,ClientRect.Top+400);
  117.   Caption:= 'Click on a cell ...';
  118.   BMP:= TBitmap.Create;
  119.   BMP.SetSize(400,400);
  120. End;
  121.  
  122. Procedure TForm1.FormClose(Sender:TObject;Var CloseAction:TCloseAction);
  123. Begin
  124.   BMP.Free;
  125. End;
  126.  
  127. END.
TinyPortal © 2005-2018