unit fifteenengine;

{$mode ObjFPC}{$H+}

interface

uses
  ClassesSysUtilsMath
  {$IFNDEF BROWSER}
    ,LCLType
  {$ENDIF}
  ;


Const
  CSIZE 15;
  BSIZE 100;
  GAP 4;
  {$IFDEF BROWSER}
    VK_UP    100;
    VK_DOWN  101;
    VK_RIGHT 102;
    VK_LEFT  103;
    COLORBLANK '#808080';
    COLOROK    '#b3ffb3';
    COLORWRONG '#ff9999';
  {$ELSE}
    COLORBLANK $00808080;
    COLOROK    $00b3ffb3;
    COLORWRONG $009999ff;
  {$ENDIF}

type Block Record
    n,
    x,
    integer;
  end;

var
  Board array [0..CSIZEof Block;
  Blank integer;

Procedure BoardInit;
procedure Shuffle;
Function  DoMove(key:word):Boolean;
Function IsSolvable:Boolean;


implementation

procedure SetBlank;
var
  iInteger;
begin
  for i:=to CSIZE do
    if Board[i].then begin
      Blank:=i;
      {$IFNDEF WINDOWS}
      writeln('blank=',i,' x=',Board[i].x,' y=',Board[i].y);
      {$ENDIF}
      Break;
    end;
end;

Procedure BoardInit;
var
  px,py,
  pbInteger;
begin
  {$IFNDEF BROWSER}
  Randomize;
  {$ENDIF}
  for pb:=to CSIZE do begin
    DivMod(pb,4,py,px);
    with Board[pbdo begin
      n:=pb+1;
      x:=px;
      y:=py;
    end;
  end;
  Board[CSIZE].n:=0;
  Blank:=CSIZE;
end;

procedure Shuffle;
var
  i,j,nInteger;
begin
  for := CSIZE downto do begin
    := Random(i);
    if not (jthen begin
      := Board[i].n;
      Board[i].:= Board[j].n;
      Board[j].:= n;
    end;
  end;
  SetBlank;
  if not IsSolvable then
    Shuffle;
end;

Function  CanMove(key:word):Boolean;
begin
  Result:=False;
  case key of
    VK_UP    if Board[Blank].then exit;
    VK_DOWN  if Board[Blank].then exit;
    VK_RIGHT if Board[Blank].then exit;
    VK_LEFT  if Board[Blank].then exit;
  end;
  Result:=True;
end;

Function DoMove(Key:word):Boolean;
var
  d:integer;
begin
  Result:=CanMove(key);
  if not Result then
    exit;
  case Key of
    VK_UP    :=  4;
    VK_DOWN  := -4;
    VK_RIGHT := -1;
    VK_LEFT  :=  1;
  end;
  Board[Blank].:= Board[Blank+d].n;
  Blank:=Blank+d;
  Board[Blank].n:=0;
end;

// https://mathworld.wolfram.com/15Puzzle.html
// https://stackoverflow.com/questions/34570344/check-if-15-puzzle-is-solvable

Function IsSolvable:Boolean;
var
  {$IFDEF DEBUG}
  sol array[0..15of integer = (1211027114145091581363); // solvable
  //sol : array[0..15] of integer = (3, 9, 1, 15, 14, 11, 4, 6, 13, 0, 10, 12, 2, 7, 8, 5); // not solvable
  //sol : array[0..15] of integer = (13, 10, 11, 6, 5, 7, 4, 8, 1, 12, 14, 9, 3, 15, 2, 0); // not solvable
  {$ENDIF}
  i,integer;
  p,integer;
begin
  {$IFDEF DEBUG}
  for i:=to CSIZE do
    Board[i].n:=sol[i];
  SetBlank;
  {$ENDIF}
  c:=0;
  p:=4-Board[Blank].y;
  for i:=to CSIZE do
    for j:=to CSIZE -do
      if (Board[i].0and (Board[j].0then
        if Board[i].Board[j].then
          inc(c);
  {$IFNDEF WINDOWS}
  writeln('c=',c,' p=',p,' odd(c)=',odd(c),' odd(p)=',odd(p));
  {$ENDIF}
  Result:=odd(cxor odd(p);
end;

end.