///////////////////////////////////////////////////////////////////////////////
//                              Lisaac Example                               //
//                                                                           //
//                   LSIIT - ULP - CNRS - INRIA - FRANCE                     //
//                                                                           //
//   This program is free software: you can redistribute it and/or modify    //
//   it under the terms of the GNU General Public License as published by    //
//   the Free Software Foundation, either version 3 of the License, or       //
//   (at your option) any later version.                                     //
//                                                                           //
//   This program is distributed in the hope that it will be useful,         //
//   but WITHOUT ANY WARRANTY; without even the implied warranty of          //
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           //
//   GNU General Public License for more details.                            //
//                                                                           //
//   You should have received a copy of the GNU General Public License       //
//   along with this program.  If not, see <http://www.gnu.org/licenses/>.   //
//                                                                           //
//                     http://isaacproject.u-strasbg.fr/                     //
///////////////////////////////////////////////////////////////////////////////
Section Header
  
  + name    := KNIGHT;
  
  - author  := "Sonntag Benoit (bsonntag@loria.fr)";

  - comment := 
  "The classic KNIGHT problem on a N times N square chessboard. \
  \The knight have to pass on all places of the chessboard once \
  \a time. For instance, here is solution for a 7 X 7 chesboard,\
  \knigth starting at position <1,1> :                          \
  \                                                             \
  \            1   28   37   24    3   26   17                  \
  \           36   39    2   27   18   11    4                  \
  \           29   42   23   38   25   16    9                  \
  \           40   35   30   19   10    5   12                  \
  \           43   22   41   32   15    8   47                  \
  \           34   31   20   45   48   13    6                  \
  \           21   44   33   14    7   46   49                  ";
  
Section Inherit
  
  - parent_object:OBJECT := OBJECT;

Section Private
  
  - chess_min:INTEGER := 3;

  - chess_max:INTEGER := 24;

  - chessboard:ARRAY2[INTEGER];

  - nb_tries:INTEGER;

  - tl:ARRAY[INTEGER] :=
  ( + result:ARRAY[INTEGER];
    result := ARRAY[INTEGER].create 1 to 8;
    result.put (-2) to 1; result.put (-1) to 2;
    result.put   1  to 3; result.put   2  to 4;
    result.put   2  to 5; result.put   1  to 6;
    result.put (-1) to 7; result.put (-2) to 8;
    result
  );

  - tc:ARRAY[INTEGER] :=
  ( + result:ARRAY[INTEGER];
    result := ARRAY[INTEGER].create 1 to 8;
    result.put   1  to 1; result.put   2  to 2;
    result.put   2  to 3; result.put   1  to 4;
    result.put (-1) to 5; result.put (-2) to 6;
    result.put (-2) to 7; result.put (-1) to 8;
    result
  );

  - knight (size, line, column:INTEGER) <-
  ( ? {size >= 3};
    ? {1 <= line};
    ? {line <= size};
    ? {1 <= column};
    ? {column <= size};
    
    chessboard := ARRAY2[INTEGER].create (1,1) to (size,size);
    chessboard.put 1 to (line,column);
    (solution (line,column)).if {
      print;
    } else {
      "Sorry, there is no solution.\n".print;
    };
    "\nNumber of tries : ".print;
    nb_tries.print;
    '\n'.print;
  );

  - solution (line,column:INTEGER) :BOOLEAN <-
  ( + value,i:INTEGER;
    + result:BOOLEAN;
    
    (chessboard.count = chessboard.item (line,column)).if {
      result := TRUE;
    } else {      
      i := 1;
      value := chessboard.item (line,column);
      {(result) || {i > 8}}.until_do {
        result := try ((line + tl.item i),(column + tc.item i),value);
        i := i + 1;
      };
    };
    result
  );

  - try (line, column, value:INTEGER) :BOOLEAN <-
  // Try to place the knight by used cross back-tracking method.
  ( + result:BOOLEAN;
    
    nb_tries := nb_tries + 1;
    (chessboard.valid_index (line,column)).if {
      (chessboard.item (line,column) = 0).if {
        chessboard.put (value+1) to (line,column);
        result := solution (line,column);
        (result).if_false {
          chessboard.put 0 to (line,column);
        };
      };
    };
    result
  );

  - ask (s:ABSTRACT_STRING, min,max:INTEGER) :INTEGER <-
  // Ask for question `s' until the answer is in range `min' `max'.
  ( + stop:BOOLEAN;
    + result:INTEGER;
    
    {stop}.until_do {
      s.print;
      IO.read_integer;
      result := IO.last_integer;
      (result < min).if {
        "Value too small.\n".print;
      }.elseif {max < result} then {
        "Value too big.\n".print;
      } else {
        stop := TRUE;
      };
    };
    result
  );

  - print <-
  // Display the cheesboard.
  ( + separator:STRING;
    
    separator := STRING.create_filled (' ',(3 * chessboard.upper1 + 1));
    separator.fill_with '-';
    separator.extend '\n';
    separator.print;
    (chessboard.lower1).to (chessboard.upper1) do { line:INTEGER;
      (chessboard.lower2).to (chessboard.upper2) do { column:INTEGER;
        (chessboard.item (line,column) < 10).if {
          "| ".print;
        } else {
          '|'.print;
        };
        chessboard.item (line,column).print;
      };
      "|\n".print;
      separator.print;
    };
  );
  
Section Public
  
  - main <-
  ( + size, line, column:INTEGER;
    
    ((COMMAND_LINE.upper >= 1) && {COMMAND_LINE.item 1.is_integer}).if {
      size := COMMAND_LINE.item 1.to_integer;
    };
    (! size.in_range chess_min to chess_max).if {
      size := ask ("Chess-board size: ",chess_min,chess_max);
    };
    ((COMMAND_LINE.upper >= 2) && {COMMAND_LINE.item 2.is_integer}).if {
      line := COMMAND_LINE.item 2.to_integer;
    };
    (! line.in_range 1 to size).if {
      line := ask ("Start line: ",1,size);
    };
    ((COMMAND_LINE.upper >= 3) && {COMMAND_LINE.item 3.is_integer}).if {
      column := COMMAND_LINE.item 3.to_integer;
    };
    (! column.in_range 1 to size).if {
      column := ask ("Start column: ",1,size);
    };
    knight (size,line,column);
  );
