puzzle.pas  |  Makefile

program Puzzle;

uses Crt;

const
  lastStone = $F;
  lastField = lastStone;
  lastField_Col = 2 + 3;
  lastField_Row = lastField_Col;
  lastDirection = 3;
  clean  = -1;

type
  pFields = array[$0..lastField_Col, $0..lastField_Row] of
             Integer;    

  pStones = array[$0..lastStone] of
             record
               used: Boolean;
               direction: Cardinal;
               clr: record
                      north, east, south, west: Cardinal;
                    end;
             end;

var
  fields: pFields;
  stones: pStones;
  delTime, errCode: Integer;

procedure ProcParams;
begin
  if ParamCount = 0 then
    delTime := 0
  else
  begin
    if ParamStr(1) = 'step' then
      delTime := -1
    else
    begin
      Val(ParamStr(1), delTime, errCode);
      if (errCode <> 0) or (delTime < 1) then
      begin
        WriteLn(stdErr, 'invalid delay time given');
        Halt(1);
      end;
    end;  
  end;
end;

procedure ShowPuzzle;
var
  c, r: Cardinal;

procedure GotoYX(y, x: Cardinal);
begin
  GotoXY(x, y);
end;

procedure DrawSquare;
var
  offC, offR: Cardinal;
begin
  offC := Succ(Pred(c) * 4);
  offR := Succ(Pred(r) * 3) + 2;
  TextColor(darkgray);
  GotoYX(offR,     offC    ); Write(#218);
  GotoYX(offR,     offC + 3); Write(#191);
  GotoYX(offR + 2, offC + 3); Write(#217);
  GotoYX(offR + 2, offC    ); Write(#192);
  GotoYX(offR + 1, offC + 1);
  if fields[c][r] > 9 then
    Write(Char(fields[c][r] - 10 + Ord('A')))
  else
    Write(fields[c][r]);
  Write(stones[fields[c][r]].Direction);

  TextColor(stones[fields[c][r]].Clr.North);
  GotoYX(offR,     offC + 1); Write(#196 #196);
  TextColor(stones[fields[c][r]].Clr.East);
  GotoYX(offR + 1, offC + 3); Write(#179);
  TextColor(stones[fields[c][r]].Clr.South);
  GotoYX(offR + 2, offC + 1); Write(#196 #196);
  TextColor(stones[fields[c][r]].Clr.West);
  GotoYX(offR + 1, offC    ); Write(#179);
end;

begin
  ClrScr;
  for c := 1 to Pred(lastField_Col) do
    for r := 1 to Pred(lastField_Row) do
      if fields[c][r] <> clean then DrawSquare;    

  if delTime <> 0 then
    if delTime = -1 then
      ReadKey
    else
      Delay(delTime);
end;


procedure Initstones;
var
  i: Cardinal;
begin
  stones[$0].Clr.North := white; stones[$0].Clr.East := blue;
  stones[$0].Clr.South := blue;  stones[$0].Clr.West := red;
  stones[$1].Clr.North := green; stones[$1].Clr.East := red;
  stones[$1].Clr.South := red;   stones[$1].Clr.West := white;
  stones[$2].Clr.North := blue;  stones[$2].Clr.East := blue;
  stones[$2].Clr.South := blue;  stones[$2].Clr.West := blue;
  stones[$3].Clr.North := green; stones[$3].Clr.East := blue;
  stones[$3].Clr.South := blue;  stones[$3].Clr.West := green;
  stones[$4].Clr.North := green; stones[$4].Clr.East := white;
  stones[$4].Clr.South := red;   stones[$4].Clr.West := red;
  stones[$5].Clr.North := green; stones[$5].Clr.East := green;
  stones[$5].Clr.South := blue;  stones[$5].Clr.West := blue;
  stones[$6].Clr.North := white; stones[$6].Clr.East := red;
  stones[$6].Clr.South := green; stones[$6].Clr.West := blue;
  stones[$7].Clr.North := green; stones[$7].Clr.East := red;
  stones[$7].Clr.South := blue;  stones[$7].Clr.West := blue;
  stones[$8].Clr.North := red;   stones[$8].Clr.East := green;
  stones[$8].Clr.South := blue;  stones[$8].Clr.West := green;
  stones[$9].Clr.North := blue;  stones[$9].Clr.East := blue;
  stones[$9].Clr.South := blue;  stones[$9].Clr.West := blue;
  stones[$A].Clr.North := white; stones[$A].Clr.East := green;
  stones[$A].Clr.South := blue;  stones[$A].Clr.West := white;
  stones[$B].Clr.North := white; stones[$B].Clr.East := blue;
  stones[$B].Clr.South := blue;  stones[$B].Clr.West := white;
  stones[$C].Clr.North := green; stones[$C].Clr.East := red;
  stones[$C].Clr.South := red;   stones[$C].Clr.West := red;
  stones[$D].Clr.North := red;   stones[$D].Clr.East := red;
  stones[$D].Clr.South := green; stones[$D].Clr.West := red;
  stones[$E].Clr.North := blue;  stones[$E].Clr.East := red;
  stones[$E].Clr.South := blue;  stones[$E].Clr.West := blue;
  stones[$F].Clr.North := blue;  stones[$F].Clr.East := green;
  stones[$F].Clr.South := red;   stones[$F].Clr.West := blue;

  for i := 0 to lastStone do
    stones[i].Direction := 0;
    stones[i].Used := false;
end;

procedure Initfield;
var
  c, r: Cardinal;
begin
  for c := 0 to lastField_Col do
    for r := 0 to lastField_Row do
      fields[c][r] := clean;
end;

procedure Applystones_Impr_2;
var
  c, r, tempClr: Cardinal;
  wStone: Integer;

label
  initWStone,
  advWStone,
  placeWStone,
  resetWStoneDir,
  checkWStoneMatch,
  checkAnothDirAvail,
  checkAnothStoneAvail,
  checkPrevFieldAvail,
  stopByError,
  stopBySuccess,
  done;
  
begin
  // point to first field
  c := 1;
  r := 1;

  // init wStone
  initwStone:
    wStone := -1;

  // advance wStone until free one is found
  advwStone:
    Inc(wStone);
    if stones[wStone].Used then
      goto advwStone;
      
  // check whether found wStone matches
  checkwStoneMatch:
    if ((fields[c][Pred(r)] = clean) or
        (stones[fields[c][Pred(r)]].Clr.South = stones[wStone].Clr.North)) and
       ((fields[c][Succ(r)] = clean) or
        (stones[fields[c][Succ(r)]].Clr.North = stones[wStone].Clr.South)) and
       ((fields[Pred(c)][r] = clean) or
        (stones[fields[Pred(c)][r]].Clr.East  = stones[wStone].Clr.West )) and
       ((fields[Succ(c)][r] = clean) or
        (stones[fields[Succ(c)][r]].Clr.West  = stones[wStone].Clr.East )) then
      goto placewStone;
  
  // check whether wStone still rotatable
  checkAnothDirAvail:
    if stones[wStone].Direction = lastDirection then
      goto resetwStoneDir;
      
  // rotate wStone into next direction
    with stones[wStone] do
    begin
      tempClr   := Clr.West;
      Clr.West  := Clr.South;
      Clr.South := Clr.East;
      Clr.East  := Clr.North;
      Clr.North := tempClr;
      Inc(Direction);
    end;
    goto checkwStoneMatch;
  
  // reset wStone color direction to zero
  resetwStoneDir:
    with stones[wStone] do
    begin
      tempClr   := Clr.West;
      Clr.West  := Clr.South;
      Clr.South := Clr.East;
      Clr.East  := Clr.North;
      Clr.North := tempClr;
      Direction := 0;
    end;
  
  // check whether another stone available
  checkAnothStoneAvail:
    if wStone = lastStone then
      goto checkPrevFieldAvail;

  // advance wStone and check whether free
    Inc(wStone);
    if stones[wStone].used then
      goto checkAnothStoneAvail;
    goto checkwStoneMatch;
    
  // place current wStone permanently and set used
  placewStone:  
    fields[c][r] := wStone;
    stones[wStone].used := true;
    ShowPuzzle; // fdpo
    
  // check whether another field available
    if (c = Pred(lastField_Col)) and (r = Pred(lastField_Row)) then
      goto stopBySuccess;
      
  // advance to next field
    if r = Pred(lastField_Row) then
    begin
      Inc(c);
      r := 1;
    end
    else
      Inc(r);
    goto initwStone;
    
  // check whether previous field available
  checkPrevFieldAvail:
    if (c = 1) and (r = 1) then
      goto stopByError;

  // reset current field to clean
    fields[c][r] := clean;

  // step back to previous field
    if r = 1 then
    begin
      Dec(c);
      r := Pred(lastField_Row);
    end
    else
      Dec(r);
      
  // set wStone to permanently placed stone and set free
    wStone := fields[c][r];
    stones[wStone].used := false;
    goto checkAnothDirAvail;

  // error caused by invalid input
  stopByError:
    GotoXY(1, 24);
    WriteLn('halted by error');
    goto done;

  // success through valid input    
  stopBySuccess:
    GotoXY(1, 24);
    WriteLn('halted by success');
  
  // nothing to do anymore
  done:
end;

begin
  HideCursor;
  ProcParams;
  InitStones;
  InitField;
  ApplyStones_Impr_2;
end.