unit ladSharU;
// This unit contains objects, variables, and functions shared
// by more than one unit of the PC Ladder program

interface

function OneDifferent(const S1, S2 : String; VAR Posn : Integer;
  VAR C1, C2 : Char) : Boolean;
function OneDifferentEZ(const S1, S2 : String) : Boolean;
function OneDifferentLoc(const S1, S2 : String) : Integer;
function NumDifferent(const S1, S2 : String) : Integer;
function GoodWord(const S : String; Len : Integer) : Boolean;


var
  IniName : String;
  NameSpec : String;
  GenHint : String;

TYPE
  TWordObj = class(TObject)
    private
      fGroup : Integer;
    protected
      fLinks : ARRAY[1..7] OF String;
      function GetLinkString : String;
    public
      constructor Create(vGroup : Integer);
      procedure AddLink(Posn : Integer; C : Char);
      property Group : Integer Read fGroup;
      property LinkString : String Read GetLinkString;
  end;

  TWordObj2 = class(TWordObj)
  private
    fCurLink,
    fCurPosn,
    fWayBack : Integer;
  public
    constructor Create(vGroup : Integer; const vLinks : String);
    function ReadyNext(const S : String) : String;
    function NextWord(const S : String) : String;
    property WayBack : Integer Read fWayBack Write fWayBack;
  end;

implementation
USES SysUtils;

constructor TWordObj.Create(vGroup : Integer);
VAR N : Integer;
begin
  Inherited Create;
  IF vGroup > -1 THEN
    fGroup := vGroup;
  FOR N := 1 TO 7 DO fLinks[N] := '';
end;

function TWordObj.GetLinkString : String;
// Return the links as a single string, of the form 1ABC2DE4F
VAR N : Integer;
begin
  Result := '';
  FOR N := 1 TO 7 DO
    IF fLinks[N] <> '' THEN
      Result := Result + Format('%d%s', [N, fLinks[N]]);
end;

procedure TWordObj.AddLink(Posn : Integer; C : Char);
// Insert the character C in the string corresponding to
// position Posn, keeping the characters in alpha order
// within the string
VAR N, Loc : Integer;
begin
  IF (Posn >= 1) AND (Posn <= 7) THEN
    begin
      Loc := 1;
      FOR N := length(fLinks[Posn]) DOWNTO 1 DO
        IF C > fLinks[Posn][N] THEN
          begin
            Loc := N+1;
            Break;
          end;
      Insert(C, fLinks[Posn], Loc);
    end;
end;

constructor TWordObj2.Create(vGroup : Integer; const vLinks : String);
VAR N, P : Integer;
begin
  Inherited Create(vGroup);
  fCurLink := -1;
  fCurPosn := -1;
  fWayBack := -1;
  N        := -1;
  FOR P := 1 TO Length(vLinks) DO
    IF (vLinks[P] >= '1') AND (vLinks[P]<='7') THEN
      N := Ord(vLinks[P])-Ord('0')
    ELSE AddLink(N, vLinks[P]);
end;

function TWordObj2.ReadyNext(const S : String) : String;
begin
  fCurLink := 1;
  fCurPosn := 0;
  Result   := NextWord(S);
end;

function TWordObj2.NextWord(const S : String) : String;
begin
  // Try for next position in current link string
  Inc(fCurPosn);
  // If past the end, try next non-empty link string
  IF fCurPosn > length(fLinks[fCurLink]) THEN
    begin
      fCurPosn := 1;
      Inc(fCurLink);
      WHILE (fCurLink <= 7) AND (length(fLinks[fCurLink])=0) DO
        Inc(fCurLink);
    end;
  IF fCurLink > 7 THEN
    begin
      Result := '';
      fCurLink := -1; fCurPosn := -1;
    end
  ELSE
    begin
      Result := S;
      Result[fCurLink] := fLinks[fCurLink][fCurPosn];
    end;
end;

function OneDifferent(const S1, S2 : String; VAR Posn : Integer;
  VAR C1, C2 : Char) : Boolean;
// Returns TRUE only if S1 and S2 differ in just one character.
// If so, Posn receives the character position, C1 gets the char
// in that position in *S2*, and C2 gets the char there in *S1*
VAR L, D  : Integer;
BEGIN
  L := Length(S1);
  D := 0;
  WHILE L > 0 DO
    begin
      IF S1[L] <> S2[L] THEN
        begin
          Inc(D);
          Posn := L;
          C1 := S2[L];
          C2 := S1[L];
        end;
      Dec(L);
    end;
  Result := D=1;
END;

function OneDifferentEZ(const S1, S2 : String) : Boolean;
VAR
  N : Integer;
  C1, C2 : Char;
begin
  Result := OneDifferent(S1, S2, N, C1, C2);
end;

function OneDifferentLoc(const S1, S2 : String) : Integer;
VAR C1, C2 : Char;
begin
  IF NOT OneDifferent(S1, S2, Result, C1, C2) THEN
    Result := -1;
end;

function NumDifferent(const S1, S2 : String) : Integer;
// Assumes S1 and S2 are the same length
VAR N : Integer;
begin
  Result := 0;
  FOR N := 1 TO Length(S1) DO
    IF S1[N] <> S2[N] THEN
      Inc(Result);
end;

function GoodWord(const S : String; Len : Integer) : Boolean;
VAR N : Integer;
begin
  IF Length(S) = Len THEN
    begin
      Result := True;
      FOR N := 1 TO Len DO
        IF NOT (S[N] IN ['A'..'Z']) THEN
          begin
            Result := False;
            Break;
          end;
    end
  ELSE Result := False;
end;

end.
