///////////////////////////////////////////////////////////////////////////////
//                              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     := FOURONLINE;

  - comment  := "FOUR WINS game";

  - author   := "Benoit Sonntag (bsonntag@loria.fr)";
  
Section Inherit  
  
  - parent_object:OBJECT := OBJECT;
  
Section Private

  //
  // Data.
  //

  - table:FAST_ARRAY2[UINTEGER_8];

  // 
  // Consultation.
  //

  - table_line cols:INTEGER :INTEGER <-
  // Get line with column.
  ( + line:INTEGER;
    ? {table.item (cols,0) = 0};

    {(line<7) && {table.item (cols,line) = 0}}.while_do {
      line:=line+1;
    };
    line-1
  );

  - match_null:BOOLEAN <-
  // True, if match null.
  ( + cols:INTEGER;
    
    {(cols<7) && {table.item (cols,0) != 0}}.while_do {
      cols:=cols+1;
    };
    cols=7
  );

  - is_success (x,y:INTEGER) :BOOLEAN <-
  // True, if win.
  ( 
    (count_origin (x,y) step   (1 ,  0) > 3) ||  // Line.
    {count_origin (x,y) step   (0 ,  1) > 3} ||  // Column.
    {count_origin (x,y) step ((-1),(-1))> 3} ||  // Diagonal 1.
    {count_origin (x,y) step ((-1),  1) > 3}     // Diagonal 2.
  );

  - count_origin (x,y:INTEGER) step (a,b:INTEGER) :INTEGER <-
  // Count alignment. 
  ( + cpt,xx,yy:INTEGER;
    + ref:UINTEGER_8;

    // Reference.
    ref:=table.item (x,y);

    // Before.
    xx:=x;
    yy:=y;
    {
      cpt:=cpt+1;  
      xx:=xx-a;
      yy:=yy-b;
    }.do_while {
      (xx.in_range 0 to 6) && 
      {yy.in_range 0 to 6} && 
      {table.item (xx,yy) = ref}
    };

    // After.
    xx:=x+a;
    yy:=y+b;
    {
      (xx.in_range 0 to 6) && 
      {yy.in_range 0 to 6} && 
      {table.item (xx,yy) = ref}
    }.while_do {
      cpt:=cpt+1;  
      xx:=xx+a;
      yy:=yy+b;
    };
  
    cpt
  );

  //
  // Play.
  //

  - play_computer:INTEGER <-
  // Get column.
  ( 
    max_level level min 65536;
    column_choice
  );

  //
  // Alpha-Beta algorithm (Min-Max).
  // 

  - level:INTEGER <- 2; 

  - column_choice:INTEGER;

  - max_level lev:INTEGER min val_min:INTEGER :INTEGER <-
  ( + cols,line,max,val:INTEGER;
    + val_line,val_cols,val_dia1,val_dia2:INTEGER;
   
    {(cols<7) && {max<=val_min}}.while_do {
      (table.item (cols,0) = 0).if {
        line:=table_line cols;
        table.put 2 to (cols,line);
        // Evaluation.
        val_line := count_origin (cols,line) step (1 ,0);
        val_cols := count_origin (cols,line) step (0,1);
        val_dia1 := count_origin (cols,line) step ((-1),(-1));
        val_dia2 := count_origin (cols,line) step ((-1),1);
        val:=(val_line>3).to_integer+(val_cols>3).to_integer+
             (val_dia1>3).to_integer+(val_dia2>3).to_integer;
        ((val!=0) || {lev=0}).if {
          // Evaluation.	  
          val:=(val<<5) + val_line+val_cols+val_dia1+val_dia2 + (lev << 8);
        } else {
          // Min evaluation.
          val:=min_level lev max max;	  
        };        
          table.put 0 to (cols,line);
          (val>=max).if { 
          max:=val;
          (lev=level).if {
            column_choice := cols; 
          };
        };
      };
      cols:=cols+1;
    };
    max
  );

  - min_level lev:INTEGER max val_max:INTEGER :INTEGER <-
  ( + cols,line,min,val:INTEGER;
    
    min:=65536;
    {(cols<7) && {min>=val_max}}.while_do {
      (table.item (cols,0) = 0).if {
        line:=table_line cols;
        table.put 1 to (cols,line);
        (is_success (cols,line)).if {
          val:=level - lev;
        } else {
          val:=max_level (lev-1) min min;
        };
        table.put 0 to (cols,line);
        (val<min).if { 
          min:=val;
        };
      };
      cols:=cols+1;
    };
    (min=65536).if {
      min:=0;
    };
    min
  );

  //
  // Display.
  //
    
  - display_text <-  
  (     
    
    '|'.print;
    '1'.to '7' do { c:CHARACTER;
      c.print;
      '|'.print;
    };
    '\n'.print;
    
    // Display Game.
    0.to 6 do { y:INTEGER;
      '|'.print;
      0.to 6 do { x:INTEGER;
        table.item (x,y)
        .when 0 then { ' '.print; }
        .when 1 then { 'X'.print; }
        .when 2 then { 'O'.print; };	
        '|'.print;	
      };
      '\n'.print;
    };    
        
    // Information.
    stat
    .when 0 then { string_tmp.copy "Player X ?"; }
    .when 1 then { string_tmp.copy "Player O ..."; }
    .when 2 then { string_tmp.copy "Winner: Player X."; } 
    .when 3 then { string_tmp.copy "Winner: Player O."; }
    .when 4 then { string_tmp.copy "Winner: Match null."; };
    string_tmp.print;
    '\n'.print;
  );
  
  - string_tmp:STRING;
  - stat:UINTEGER_8;
  
Section Public  
  
  - main <-
  ( + catch:CHARACTER;
    + col,line:INTEGER;
        
    // Initialisation.
    table := FAST_ARRAY2[UINTEGER_8].create (7,7);        
    string_tmp := STRING.create 80;
    
    display_text;    
    {stat < 2}.while_do {
      (stat = 0).if {
        //
        // Humain player.
        //
        {
          IO.read_character;
          catch:=IO.last_character;
        }.do_until {catch.in_range '1' to '7'};
        col := catch.decimal_value - 1;	
        (table.item (col,0) = 0).if {	
          // Put.
          line:=table_line col;	  
          table.put 1 to (col,line);	  
          // Detect success.
          (is_success (col,line)).if {
            stat := 2;
            display_text;
          }.elseif {match_null} then {
            stat := 4;
            display_text;
          } else {
            //
            // Computer player.
            //
            stat := 1;
            display_text;
            col := play_computer;
            // Put.
            line:=table_line col;	    
            table.put 2 to (col,line);	  
            // Detect success.
            (is_success (col,line)).if {
              stat := 3;
            }.elseif {match_null} then {
              stat := 4;	      
            } else {
              stat := 0;
            };
            display_text;
          };
        };
      } else {
        //
        // New game.
        //
        table.clear_all;
        stat := 0;
        display_text;
      };
    };        
  );  
