/* This file is part of cardwords
   (c) 1998 1999 Tobias Peters
   see file COPYING for the copyright terms.
   
   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 2 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, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

// cardwords_dicbot_cardtable.hh

/* Classes containing _DicBot_ and files containing _dicbot_ indicate
   classes and files for dicbot. dicbot will be the dictionary and
   robot process started by the cardwords server.

   The server delegates the following tasks to dicbot:

   - Check if a move is valid. This means:
   . * Check if there is enough space on the card-table to actually lay the
   .   cards.
   . * Check if all words created by that move are valid. This can be
   .   disabled.
   - Calculate the score of a move. This contains a validity check as above.
   - Calculate the best possible move for a given set of cards. The best
   . possible move means the one that gets the most points.
   . This contains several score calculations as above.
*/

// This file defines The class CardWords_DicBot_CardTable.
// This class contains all information dicbot wants to have about the
// card-table.


/* The format of a card-table description, as used by
   ostream& << CardWords_DicBot_CardTable&   and   read_card_table(istream&):

   <width> * <height>
   (<starting_column>,<starting_row>)
   [(<column>,<row>) [L <value>] [W <value>] [C <value>] [B] [T <card description>];]*
   (<end_column>,<end_row>)

   <...> : ... is meta name
   [...] : ... can be left out
   [...]*: ... can be there zero, one ore more times

   width and height are unsigned ints. They define the size of the card-table.
   starting_column  The first move onto the card-table must place a card in
   .                this column if starting_column < width.
   starting_row     The first move onto the card-table must place a card in
   .                this row if starting_row < height.
   .                starting_column and starting_row together are in a
   .                CardWords_CardTableLocation format. If both values are
   .                valid, they define a starting card-table cell together.
   column and row   define a valid card-table cell with 0<=column<width and
   .                0<=row<height. This is in a CardWords_CardTableLocation
   .                format.
   * ( , ) ;        literal asterix, parenthesis, comma and semicolon.
   .                ( , and ) belong to CardWords_CardTableLocation format.
   L W C T          literal L, W, C and T; the next will be a
   .                letter factor, word factor, cell points value or card
   .                description respectively.
   B                flag for a blocked cell.
   value            an int.
   card description a valid card description as produced by
   .                ostream& << CardWords_CardDescription& and recognized
   .                by istream& >> CardWords_CardDescription&.
   .                If T is left out, <no card> is the default (which
   .                cannot yet be expressed with a card description).
   .                This format is described in cardwords_cardbase.hh.
   end_column, end_row
   .                This is the end marker. If column or row are out of
   .                range, the end of the description is reached.
   .                This is in a CardWords_CardTableLocation format.


   Card-table cells not mentioned in the description are assumed to have
   letter factor DEFAULT_LETTER_FACTOR, word factor DEFAULT_WORD_FACTOR,
   cell points DEFAULT_CELL_POINTS, no card and are not blocked.
   DEFAULT_LETTER_FACTOR, DEFAULT_WORD_FACTOR should be 1,
   DEFAULT_CELL_POINTS should be 0.
*/

#ifndef CARDWORDS_DICTBOT_TABLE_HH
#define CARDWORDS_DICTBOT_TABLE_HH

#include "cardwords_cardtablelocation.hh"
#include "cardwords_dicbot_cardtablecell.hh"
#include "cardwords_matrix.hh"
#include "cardwords_move.hh"
#include "cardwords_dictionary.hh"
#include "cardwords_bonustable.hh"

#define MOVE_VALID (-1)
#define MOVE_INVALID (-2)
#define IS_MOVE_VALID(move) ((move) == MOVE_VALID)
#define IS_MOVE_INVALID(move) ((move) != MOVE_VALID)

class CardWords_DicBot_CardTable :
  public CardWords_Matrix<CardWords_DicBot_CardTableCell>
{
public:
  CardWords_DicBot_CardTable (size_t columns,
                              size_t rows,
                              CardWords_CardTableLocation start);

  void 
  add_card (CardWords_CardTableLocation, const CardWords_CardDescription &);

  void 
  remove_card (CardWords_CardTableLocation);

  // Returns -1 (MOVE_VALID) if the move is valid.
  // Returns -2 (MOVE_INVALID) if the move is invalid because the move is not
  // in the dictionary.
  // Returns 0<= x < move.size() if the move's x'th card led to an invalid
  // side word.
  // If the given pointer to CardWords_String is not 0, sets CardWords_String
  // to the word that was not in the dictionary.
  int
  check_move (const CardWords_Move & move,
              const CardWords_Dictionary * dict = 0,
              CardWords_String * false_word = 0);

  // Returns -1 (MOVE_VALID) if the move is valid.
  // Returns -2 (MOVE_INVALID) if the move is invalid because the move is not
  // in the dictionary.
  // Returns 0<= x < move.size() if the move's x'th card led to an invalid
  // side word.
  // Writes the score of that move into the int referenced by the second
  // argument, if the move is valid.
  // If false_word is not 0, it is possible to differentiate between a move
  // that is only invalid because one of the new words are not in the
  // dictionary and a move that is is invalid because of formal reasons, like
  // not touching another card or not obeying the start condition:
  // If count_move sets *false_word to a word, than this word would have been
  // created and is not in the dictionary. The return value in this case is
  // either -2, if the primary word fails, or >= 0, if a sideword fails.
  // If count_move does not touch *false_word (make it empty to detect this
  // difference), and returns MOVE_INVALID, then formal reasons forbid this
  // move.
  // If the move is valid (MOVE_VALID is returned), *false_word is also left
  // untouched.
  int
  count_move (const CardWords_Move &,
              int * score,
              const class CardWords_Dictionary * dict= 0,
              CardWords_String * false_word = 0);

  // the same as count_move, but actually lays the letters onto the card-table
  int
  add_move (const CardWords_Move & move,
            int * score,
            const CardWords_Dictionary * dict = 0,
            CardWords_String * false_word = 0);

  // counts the number of free places after this field
  size_t
  count_free (CardWords_CardTableLocation, CardWords_Direction) const;

  // checks if a neighbour of this location in the given direction carries a
  // card
  bool
  has_neighbour (CardWords_CardTableLocation, CardWords_Direction) const;

  // return how many cards are on the card-table:
  size_t get_no_of_contained_cards(void) const;
  bool is_empty (void) const {return get_no_of_contained_cards() == 0U;}

  // Return the starting_pos. If one of the values is out of range, we only
  // have a starting row/column. If both values are out of range, there is no
  // restriction where to place the first move.
  CardWords_CardTableLocation get_starting_pos(void) const;

  // setting the bonus table:
  void set_bonus_table (const CardWords_BonusTable *);
  
private:
  // Same comment as to check_move applies.
  // Users of this class do not need to know about the additional parameter,
  // that's why we have check_move and private_check_move.
  int
  private_check_move (const CardWords_Move & move,
                      const class CardWords_Dictionary * dict,
                      CardWords_String * false_word,
                      bool recursive);

  // Same comment as to count_move applies.
  // Users of this class do not need to know about the additional parameter,
  // that's why we have count_move and private_count_move.
  int
  private_count_move (const CardWords_Move &,
                      int * score,
                      const class CardWords_Dictionary * dict,
                      CardWords_String * false_word,
                      bool recursive);
  size_t no_of_contained_cards;
  CardWords_CardTableLocation starting_pos;

  const CardWords_BonusTable * bonus_table;
};

// read card-table from stream
// see comment at end of file for an explanation of the format
CardWords_DicBot_CardTable *
read_card_table( class istream & );

// output card-table to stream in a format accepted by read_card_table()
// see comment at end of file for an explanation of the format
ostream & operator <<(class ostream &, const CardWords_DicBot_CardTable &);

inline
CardWords_DicBot_CardTable::
CardWords_DicBot_CardTable (size_t columns,
                            size_t rows,
                            CardWords_CardTableLocation start)
  : CardWords_Matrix<CardWords_DicBot_CardTableCell>(columns,rows),
    no_of_contained_cards(0),
    starting_pos(start),
    bonus_table(0)
{
  
}

// setting the bonus table:
inline void CardWords_DicBot_CardTable::
set_bonus_table (const CardWords_BonusTable *bt)
{
  bonus_table = bt;
}
  

// checks if a neighbour of this location in the given direction carries a
// card
inline  bool
CardWords_DicBot_CardTable::has_neighbour (CardWords_CardTableLocation loc,
                                           CardWords_Direction dir) const
{
  bool retval = false;
  if (dir == horizontal) {
    if ( loc.first > 0 )
      retval |= (*this)(loc.first-1,loc.second).get_containsCard();
    if ( loc.first < get_width()-1 )
      retval |= (*this)(loc.first+1,loc.second).get_containsCard();
  }
  else {
    if ( loc.second > 0 )
      retval |= (*this)(loc.first,loc.second-1).get_containsCard();
    if ( loc.second < get_height()-1 )
      retval |= (*this)(loc.first,loc.second+1).get_containsCard();
  }
  return retval;
}

inline int
CardWords_DicBot_CardTable::
check_move (const CardWords_Move & move,
            const class CardWords_Dictionary * dict = 0,
            CardWords_String * false_word = 0)
{
  return private_check_move(move, dict, false_word, false);
}

inline int
CardWords_DicBot_CardTable::
count_move (const CardWords_Move & move,
            int * score,
            const class CardWords_Dictionary * dict= 0,
            CardWords_String * false_word = 0)
{
  return private_count_move(move, score, dict, false_word, false);
}

// return how many cards are on the card-table:
inline size_t
CardWords_DicBot_CardTable::get_no_of_contained_cards(void) const
{
  return no_of_contained_cards;
}

// Return the starting_pos. If one of the values is out of range, we only
// have a starting row/column. If both values are out of range, there is no
// restriction where to place the first move.
inline CardWords_CardTableLocation
CardWords_DicBot_CardTable::get_starting_pos(void) const
{
  return starting_pos;
}
#endif

