unit U_MagicMatrix;
{Copyright  © 2002, Gary Darby,  www.DelphiForFun.org
 This program may be used or modified for any non-commercial purpose
 so long as this original notice remains in place.
 All other rights are reserved
 }

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, Spin, Grids, ComCtrls;

type
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    MagicEdt: TSpinEdit;
    CreateBtn: TButton;
    Label1: TLabel;
    SizeGrp: TRadioGroup;
    Memo1: TMemo;
    ResultLbl: TLabel;
    ExplainBtn: TButton;
    StatusBar1: TStatusBar;
    procedure SizeGrpClick(Sender: TObject);
    procedure CreateBtnClick(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure StringGrid1Click(Sender: TObject);
    procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure ExplainBtnClick(Sender: TObject);
  public
    { Public declarations }
    clickcount:integer;
    sum:integer;
    sumstr:string;
    gridmask:array of array of integer;
    explainmode:boolean; {set when playing the matrix on the explaindlg form}
    procedure makegrid(sender:TStringGrid);
  end;

var    Form1: TForm1;

implementation

uses U_Explain;

{$R *.DFM}

{**************** AdjustGridSize *************}
procedure adjustGridSize(grid:TStringGrid);
{Adjust borders of grid to just fit cells}
var   w,h,i:integer;
begin
  with grid do
  begin
    w:=0;
    for i:=0 to colcount-1 do w:=w+colwidths[i];
    width:=w;
    repeat width:=width+1 until fixedcols+visiblecolcount=colcount;
    h:=0;
    for i:=0 to rowcount-1 do h:=h+rowheights[i];
    height:=h;
    repeat height:=height+1 until fixedrows+visiblerowcount=rowcount;
    invalidate;
  end;
end;

{**************** SizeGrpClick ***********}
procedure TForm1.SizeGrpClick(Sender: TObject);
{user changed the matrix size}
begin
  with stringgrid1 do
  begin
    rowcount:=sizegrp.itemindex+3;
    colcount:=rowcount;
    adjustgridsize(stringgrid1);
    makegrid(stringgrid1);
  end;
end;

{********************* MakeGrid **************}
procedure Tform1.makegrid(sender:TStringGrid);
{Create a magic matrix in the StringGrid passed as "sender"}
var
  n, count, sum:integer;
  val1, val2: array of integer;
  i,j,range:integer;
  minval, tries:integer;
begin
  tries:=0;
  repeat  {modification to generate only positive numbers, if possible}
    count:=sender.rowcount;
    setlength(gridmask,count,count);
    for i:=0 to count-1 do
    for j:=0 to count-1 do
    begin
      if explainmode and ((i=0) or (j=0)) then gridmask[i,j]:=2
      else gridmask[i,j]:=0;
    end;
    resultlbl.caption:='';
    clickcount:=0;
    if explainmode then exit; {no need to generate values for explaination grid}
    setlength(val1,count);
    setlength(val2,count);
    n:= magicEdt.value;
    sum:=0;
    if n>10 then range:=n else range:=10;
    begin
      for i:=0 to count-1 do
      begin
        val1[i]:=random(range div 2)+1;
        sum:=sum+val1[i];
        if sum>n then
        begin
          val1[i]:=-val1[i];
          sum:=sum+2*val1[i];
        end;
        val2[i]:=random(range div 5)+1;
        sum:=sum+val2[i];
        if sum>n then
        begin
          val2[i]:=-val2[i];
          sum:=sum+2*val2[i];
        end;
      end;
      val2[count-1]:=n-sum+val2[count-1];
    end;
    {now generate the table}
    minval:=9999;
    with sender do
    for i:= 0 to colcount-1 do
    for j:=0 to rowcount-1 do
    begin
      cells[i,j]:=inttostr(val1[i]+val2[j]);
      if val1[i]<minval then minval:=val1[i];
      if val2[j]<minval then minval:=val2[j];
    end;
    inc(tries);
  until (tries>1000) or (minval>=0);{try 1000 times or until all numbers are >=0}
end;


{***************** CreateBtnClick *********}
procedure TForm1.CreateBtnClick(Sender: TObject);
begin
  makegrid(StringGrid1);
end;

{************** FormActivate *********}
procedure TForm1.FormActivate(Sender: TObject);
begin
  randomize;
  makegrid(stringgrid1);
  adjustgridsize(stringgrid1);
end;

{****************** StringGrid1Click *********}
procedure TForm1.StringGrid1Click(Sender: TObject);
{User clicked the grid -
  set gridmask to draw clicked cell with green background and
  blank out other cells in the same row and column.
  In "Explainmode" the grid is being shown with generating values displayed
  in row 0 and column 0, these values will be remain displayed as other cells
  are clicked.
 }

var
  x:integer;
  signval:string;
  i:integer;
  start:integer;
  lbl:TLabel;
begin
  with sender as Tstringgrid do
  if gridmask[col,row]=0 then {0 ==> clickable cell}
  begin
    if gridmask[col,row]=0 then
    if clickcount=0 then sumstr:='';;
    inc(clickcount);   {count the clicks}
    {add the clicked value to the equation being formed}
    x:=strtoint(cells[col,row]);
    sum:=sum+x;
    if x<0 then signval:='-' else signval:='+';
    signval:=signval+inttostr(abs(x));

    if explainmode then
    begin
      lbl:=Explaindlg.resultlbl;
      start:=1;
    end
    else
    begin
      lbl:=resultlbl;
      start:=0;
    end;

    sumstr:=sumstr+signval;
    if clickcount=rowcount-start then
    begin
      sumstr:=sumstr+'=' +inttostr(sum);
      clickcount:=0;
      sum:=0;
    end;
    if sumstr[1]='+' then sumstr[1]:=' ';
    lbl.caption:=Sumstr;
    gridmask[col,row]:=1;
    for i:=start to colcount-1 do if i<>col then gridmask[i,row]:=-1;
    for i:=start to rowcount-1 do if i<>row then gridmask[col,i]:=-1;
    invalidate; {force complete redraw of the grid cells}
  end;
end;

{************* StringGrid1DrawCll ***********}
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
  var s:string;
{DRaw cells based on current gridmask values}
begin
  with sender as Tstringgrid do
  begin
    if gridmask[acol,arow]<0 then Canvas.Brush.Color := clgray
    else if gridmask[acol,arow]=0 then Canvas.Brush.Color := clwindow
    else if gridmask[acol,arow]>1 then Canvas.Brush.Color := clyellow
    else Canvas.Brush.Color := cllime;
    Canvas.FillRect(Rect);
    s:=cells[acol,arow];
    if gridmask[acol,arow]>=0
    then canvas.textout(rect.left+2, rect.top+2,s);
  end;

end;

{**************** ExplainBtnClick **********}
procedure TForm1.ExplainBtnClick(Sender: TObject);

begin
  explainmode:=true;
  makegrid(ExplainDlg.stringgrid1); {make a grid and gridmask for the explaination matrix}
  ExplainDlg.showmodal;
  explainmode:=false;
  makegrid(stringgrid1);  {make the real matrix again}
end;

end.