Problema damelor

Problema damelor

Postby DarkByte » 19 Apr 2011, 01:09

M-am gandit, recent, sa implementez niste algoritmi clasici in niste programe mai ... vizuale, pentru a facilita intelegerea tehnicilor de programare si a problemelor.

Am sa incep cu problema damelor, o aplicatie clasica de backtracking (in varianta in care se doreste gasirea tuturor solutiilor, evident).

Un exemplu animat al cautarii solutiilor (imagine luata de pe Wikipedia):
Image

Programul meu afiseaza damele pe masura ce sunt cautate, aratand fiecare pas al cautarii, iar daca vreo dama asezata pe tabla inaintea damei curente o ataca pe aceasta, un chenar rosu va fi afisat in jurul damei curente. Damele pot ataca pe rectangular (orizontal sau vertical) si pe diagonala (daca nu stiti juca sah, acum este momentul sa va apucati :P). Am folosit un backtracking recursiv pentru ca mi s-a parut mai usor de scris ... so sue me :P - chiar si asa, comentariile din cod ar trebui sa va ajute sa intelegeti. If all else fails, sunteti invitati sa puneti intrebari :)

O imagine cu programul:
Image

Asa cum se observa si din screenshot, programul permite oprirea, pauzarea sau setarea vitezei de afisare a algoritmului. Thought they might come in handy. Programul e limitat sa foloseasca o tabla minima de 4x4 si o tabla maxima de 12x12 - nu exista solutii pentru table mai mici (decat la 1x1 :P) si, cu siguranta, nu veti sta sa "urmariti" cautarea tuturor solutiilor pentru o tabla de 12x12 (nici macar la viteza maxima) :P

Acestea fiind zise, in speranta ca v-am convins sa cumparati ( :P ), urmeaza codul sursa al programului:

  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7.   Dialogs, StdCtrls, ComCtrls, Spin, ExtCtrls, ShellAPI;
  8.  
  9. type
  10.   TForm1 = class(TForm)
  11.     Label1: TLabel;
  12.     seDame: TSpinEdit;
  13.     Label2: TLabel;
  14.     lblCountSol: TLabel;
  15.     btnStart: TButton;
  16.     imgQueenW: TImage;
  17.     imgQueenB: TImage;
  18.     imgBackW: TImage;
  19.     imgBackB: TImage;
  20.     imgBoard: TImage;
  21.     btnStop: TButton;
  22.     lblCredits: TLabel;
  23.     imgCheck: TImage;
  24.     seWait: TSpinEdit;
  25.     Label3: TLabel;
  26.     btnPause: TButton;
  27.     procedure btnStartClick(Sender: TObject);
  28.     procedure FormCreate(Sender: TObject);
  29.     procedure btnStopClick(Sender: TObject);
  30.     procedure lblCreditsClick(Sender: TObject);
  31.     procedure btnPauseClick(Sender: TObject);
  32.   private
  33.     { Private declarations }
  34.   public
  35.     { Public declarations }
  36.     procedure CalculateSolution(aLine: Integer);
  37.     procedure DrawTable(aCount: Integer; inCheck: Boolean = False; aFinal: Boolean = False);
  38.   end;
  39.  
  40. var
  41.   Form1: TForm1;
  42.  
  43. var solution : array [1 .. 12] of Integer; // it takes too long for more, anyway!
  44.     linCount: integer;
  45.     solCount : integer;
  46.     DoPause: Boolean;
  47.     MustStop: Boolean;
  48.     WaitInterval: Cardinal;
  49.  
  50. implementation
  51.  
  52. {$R *.dfm}
  53.  
  54. function PositionOK(aLin, aCol : Integer): Boolean;
  55. var
  56.   i: Integer;
  57. begin
  58.   Result := True;
  59.   if aLin = 1 then // it's on the first line, for cryin' out loud :)
  60.     Exit;
  61.  
  62.   for i := 1 to aLin - 1 do // check for attacks on this queen from EVERY other queen on the table
  63.     begin
  64.       if not Result then
  65.         break;
  66.  
  67.       if (abs(aCol - solution[i]) = abs(aLin - i)) or // check diagonally ...
  68.          (aCol = solution[i]) then                    // and vertically
  69.         Result := False;
  70.     end;
  71. end;
  72.  
  73. procedure TForm1.CalculateSolution(aLine: Integer); // recursive backtracking
  74. var i: Integer;
  75. begin
  76.   if MustStop then
  77.     Exit;
  78.   while DoPause do
  79.     Application.ProcessMessages;
  80.  
  81.   if (aLine <= linCount) // while we didn't find a solution
  82.     then
  83.       begin
  84.         for i := 1 to linCount do // iterate through all possible positions on this level
  85.           begin
  86.             if MustStop then
  87.               Exit;
  88.             while DoPause do
  89.               Application.ProcessMessages;
  90.  
  91.             solution[aLine] := i;
  92.             DrawTable(aLine, not PositionOK(aLine, i));
  93.  
  94.             if PositionOK(aLine, i) then // if this position is suitable, use it
  95.               begin
  96.                 solution[aLine] := i;
  97.                 CalculateSolution(aLine + 1); // and continue looking for the solution
  98.                                               // then we return and continue looking for another solutions
  99.                 if MustStop then
  100.                   Exit;
  101.                 while DoPause do
  102.                   Application.ProcessMessages;
  103.  
  104.                 DrawTable(aLine - 1);
  105.                 solution[aLine] := 0;
  106.               end;
  107.           end
  108.       end
  109.     else
  110.       begin // we found a solution !!!
  111.         if MustStop then
  112.           Exit;
  113.         while DoPause do
  114.           Application.ProcessMessages;
  115.  
  116.         inc(solCount);
  117.         lblCountSol.Caption := IntToStr(solCount);
  118.         DrawTable(aLine, False, True);
  119.       end;
  120. end;
  121.  
  122. procedure TForm1.btnStartClick(Sender: TObject);
  123. begin
  124.   btnStart.Enabled := False;
  125.   btnStop.Enabled := True;
  126.   seDame.Enabled := False;
  127.   btnPause.Enabled := True;
  128.   MustStop := False;
  129.   DoPause := False;
  130.  
  131.   lblCountSol.Caption := '0';
  132.   solCount := 0;
  133.   linCount := seDame.Value;
  134.  
  135.   Self.ClientWidth := imgBoard.Left + linCount * imgBackW.Width + 16;
  136.   Self.ClientHeight := imgBoard.Top + linCount * imgBackW.Height + 32;
  137.   imgBoard.Picture := nil;
  138.   imgBoard.Width := linCount * imgBackW.Width;
  139.   imgBoard.Height := linCount * imgBackW.Height;
  140.  
  141.   DrawTable(0);
  142.   CalculateSolution(1);
  143.   DrawTable(0);
  144.  
  145.   lblCountSol.Caption := IntToStr(solCount);
  146.  
  147.   btnStart.Enabled := True;
  148.   btnStop.Enabled := False;
  149.   seDame.Enabled := True;
  150.   btnPause.Enabled := False;
  151. end;
  152.  
  153. procedure TForm1.DrawTable(aCount: Integer; inCheck: Boolean = False; aFinal: Boolean = False);
  154. var
  155.   lin, col: Integer;
  156. begin
  157.   WaitInterval := seWait.Value;
  158.  
  159.   // drawing the chess board
  160.   for lin := 0 to linCount - 1 do
  161.     for col := 0 to linCount - 1 do
  162.       begin
  163.         if odd(lin + col + 1)
  164.           then imgBoard.Canvas.Draw(col * imgBackW.Width, lin * imgBackW.Height, imgBackW.Picture.Graphic)
  165.           else imgBoard.Canvas.Draw(col * imgBackW.Width, lin * imgBackW.Height, imgBackB.Picture.Graphic);
  166.       end;
  167.  
  168.   // draw a red square if the current queen is attacked by a previously set queen
  169.   if inCheck then
  170.     imgBoard.Canvas.Draw((solution[aCount] - 1) * imgBackW.Width, (aCount - 1) * imgBackW.Height, imgCheck.Picture.Graphic);
  171.  
  172.   // drawing the queens
  173.   for lin := 1 to aCount do
  174.     begin
  175.       if (lin < aCount) and (not aFinal)
  176.         then imgBoard.Canvas.Draw((solution[lin] - 1) * imgBackW.Width + 4, (lin - 1) * imgBackW.Height + 4, imgQueenW.Picture.Graphic)
  177.         else imgBoard.Canvas.Draw((solution[lin] - 1) * imgBackW.Width + 4, (lin - 1) * imgBackW.Height + 4, imgQueenB.Picture.Graphic);
  178.     end;
  179.  
  180.   Application.ProcessMessages;
  181.   if aFinal
  182.     then Sleep(WaitInterval * 4)
  183.     else Sleep(WaitInterval);
  184. end;
  185.  
  186. procedure TForm1.FormCreate(Sender: TObject);
  187. begin
  188.   DoubleBuffered := True; // ensure less flickering
  189. end;
  190.  
  191. procedure TForm1.btnStopClick(Sender: TObject);
  192. begin
  193.   MustStop := True;
  194. end;
  195.  
  196. procedure TForm1.btnPauseClick(Sender: TObject);
  197. begin
  198.   DoPause := not DoPause;
  199. end;
  200.  
  201. procedure TForm1.lblCreditsClick(Sender: TObject);
  202. begin
  203.   ShellExecute(Handle, 'open', 'http://www.bitcell.info', nil, nil, SW_SHOWNORMAL);
  204. end;
  205.  
  206. end.


Pentru mai multe informatii despre aceasta problema, aruncati un ochi pe Wikipedia.

Sursa (imagini incluse) si executabilul in atasament.

Bafta
7p / 2 votes
Attachments
queens_src.zip
(15.62 KiB) Downloaded 51 times
queens_exe.zip
(213.92 KiB) Downloaded 57 times
User avatar
DarkByte
11011011
 
Joined: 29 Dec 2009
Status: 140

Return to Tutoriale Delphi

Who is online

Users browsing this forum: No registered users and 0 guests