unit PegDatastructures;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils;
const
C_MAX = 7; // max board size 7x7
type
TCellNums = 1..C_MAX;
TCellType = (ctNoAccess, ctEmpty, ctPeg);
TPegCells = array[TCellNums, TCellNums] of TCellType;
TCellPosition = record
Row: TCellNums;
Col: TCellNums;
end;
{ TPegSolitaire }
TPegSolitaire = class
private
FSize : TCellNums;
PegCells: TPegCells;
function GetCell(const ARow, ACol: TCellNums): TCellType;
procedure SetCell(const ARow, ACol: TCellNums; AValue: TCellType);
function GetCell(const APosition: TCellPosition): TCellType;
public
constructor Create(const ASize: TCellNums);
procedure InitializeBoard(const ABoard: Ansistring);
procedure Leap(const AFromCell, AToCell: TCellPosition);
property Cell[const ARow, ACol: TCellNums]: TCellType read GetCell write SetCell;
property Size: TCellNums read FSize;
end;
implementation
{ TPegSolitaire }
function TPegSolitaire.GetCell(const ARow, ACol: TCellNums): TCellType;
begin
Result := PegCells[ARow, ACol];
end;
procedure TPegSolitaire.SetCell(const ARow, ACol: TCellNums; AValue: TCellType);
begin
PegCells[ARow, ACol] := AValue;
end;
function TPegSolitaire.GetCell(const APosition: TCellPosition): TCellType;
begin
Result := Cell[APosition.Col, APosition.Row];
end;
constructor TPegSolitaire.Create(const ASize: TCellNums);
var
iRow, iCol: Integer;
begin
// Store the size of the board locally
FSize := ASize;
// Initialize all cells to 'not accessible'
for iRow := 1 to Size do
for iCol := 1 to Size do
Cell[iRow, iCol] := ctNoAccess;
end;
procedure TPegSolitaire.InitializeBoard(const ABoard: Ansistring);
var
Lst : TStringList;
iRow, iCol: Integer;
s : String;
begin
// Create a list with the board text in it. This will split all lines
// into individual lines, because of the LineEnding 'splitter'.
Lst := TStringList.Create;
Lst.Text := ABoard;
// Process all lines one at a time
for iRow := 0 to Lst.Count - 1 do begin
if iRow < Size then begin // make sure there is no overflow in the rows
// Process a single line of text
s := Lst[iRow];
for iCol := 1 to Length(s) do begin
if iCol <= Size then begin // check overflow in the cols
case s[iCol] of
' ': Cell[iRow + 1, iCol] := ctNoAccess;
'.': Cell[iRow + 1, iCol] := ctEmpty;
'o': Cell[iRow + 1, iCol] := ctPeg;
end;
end;
end;
end;
end;
// Clean up the list
FreeAndNil(Lst);
end;
procedure TPegSolitaire.Leap(const AFromCell, AToCell: TCellPosition);
var dx, dy: integer;
JumpedCell: TCellPosition;
begin
// Verify that the start cell is occupied and the target cell is empty
// If not, leave the procedure via the EXIT.
if (GetCell(AFromCell) <> ctPeg) or (GetCell(AToCell) <> ctEmpty) then EXIT;
// Calculate the horizontal and vertical distance between the cells
dx := abs(AFromCell.Col - AToCell.Col);
dy := abs(AFromCell.Row - AToCell.Row);
// A valid move has one direction equal to zero and the other equal to 2
if ((dx = 2) and (dy = 0)) or ((dx = 0) and (dy = 2)) then
begin
// Determine the position of the jumped cell; it's in the middle
JumpedCell.Col := (AFromCell.Col + AToCell.Col) div 2;
JumpedCell.Row := (AFromCell.Row + AToCell.Row) div 2;
// Final check: is there a peg in the jumped cell?
if GetCell(JumpedCell) = ctPeg then
begin
// Jump: clear the FromCell, empty the jumped cell and populate the ToCell
Cell[AFromCell.Row, AFromCell.Col] := ctEmpty;
Cell[JumpedCell.Row, JumpedCell.Col] := ctEmpty;
Cell[AToCell.Row, AToCell.Col] := ctPeg;
end;
end;
end;
end.