//
//
#ifndef	FOURBY3_H
#define	FOURBY3_H

#include "mdp.h"
#include "clrandom.h"

typedef	int FourBy3Action;
const int West = 0;
const int East = 1;
const int North	= 2;
const int South	= 3;

class FourBy3State
{
public:
  // constructors
  FourBy3State(int a1 =	0, int a2 = 0):	n1(a1),	n2(a2) {};

  int operator== (FourBy3State & other)	{
    return (n1 == other.n1 && n2 == other.n2);
  };

  // data
  int n1, n2;
};

istream	& operator >> (istream & , FourBy3State	&);
ostream	& operator << (ostream & , FourBy3State	&);


class FourBy3StateIterator: public iterator<FourBy3State>
{
public:
  FourBy3StateIterator(): current(0,0) {init();};

  // iterator protocol
  virtual int init() { current.n1 = current.n2 = 0; return !(*this);};
  virtual int operator!() {return (current.n1 <= 3 && current.n2 <= 2);};
  virtual FourBy3State & operator () ()	{ return current; };
  virtual int operator++()
  {
    current.n2++;
    if (current.n2 > 2)	{
      current.n2 = 0;
      current.n1++;
    }
    return !(*this);
  }
  virtual void operator	= (FourBy3State	newValue) {
    current = newValue;
  }
protected:
  FourBy3State current;
};


class FourBy3Problem: public Problem<FourBy3State, FourBy3Action>
{
public:
  float	gamma;
  clrandom rngenerator;

  FourBy3Problem(unsigned int seed1 = 718373,
		 unsigned int seed2 = 373601):
    rngenerator(seed1,seed2),
    gamma(1.0),
    si(4*3),
    pi(4*3)
  {
    buildModel();
  };

  virtual float	discountFactor() { return gamma; };

  virtual unsigned int maxState() {
    return 4 * 3;
  };
  virtual unsigned int maxAction() {
    return 4;
  }

  virtual FourBy3State nullState() { return FourBy3State(0,0); }

  virtual FourBy3Action	nullAction() { return 0; };

  // return an iterator	that iterates over the states
  virtual iterator<FourBy3State> * stateIterator() {
    return new FourBy3StateIterator;
  }

  virtual FourBy3Action	randomAction() { return	rngenerator.between(0, 3); }

  virtual iterator<FourBy3Action> * actionIterator() {
    return new rangeIterator(0,	3);
  }

  virtual list<SuccessorInfo<FourBy3State, FourBy3Action> *> *
  successors(FourBy3State s)
  {
    return &si[stateIndex(s)];
  };

  virtual list<PredecessorInfo<FourBy3State, FourBy3Action> *> *
  predecessors(FourBy3State s)
  {
    return &pi[stateIndex(s)];
  };

  virtual unsigned int stateIndex(FourBy3State s) {
    return s.n1	* 3 + s.n2;
  };

  virtual unsigned int actionIndex(FourBy3Action a) {
    return a;
  };

  virtual void printState(ostream & str, FourBy3State &	s) {
    str	<< s;
  };
  virtual void printAction(ostream & str, FourBy3Action	& a) {
    str	<< a;
  };

  // initialize	to a random starting state
  virtual void init(FourBy3State & s) {
    s.n1 = rngenerator.between(0,3);
    if (s.n1 ==	0 || s.n1 == 2)	{
      s.n2 = rngenerator.between(0,2);
    }
    else if (s.n1 == 1)	{
      // avoid the "hole"
      s.n2 = rngenerator.between(0,1);
      if (s.n2 == 1) s.n2 = 2;
    }
    // never start in a	terminal state
    else s.n2 =	0;
  };

  virtual int applicable(FourBy3Action & a, FourBy3State & s) {
    return 1;
  };

  virtual int terminated(FourBy3State &	s) {
    return (s.n1 == 3 && (s.n2 == 1 || s.n2 == 2));
  }

  virtual float	execute(FourBy3Action &	a, FourBy3State	& s);

  virtual clrandom & rng() { return rngenerator; };


  // construct the model, fill in the following	two vectors.
  void buildModel();
  void UpdateInverseModel(FourBy3State &, FourBy3Action	&, FourBy3State	&, float);
  vector<list<SuccessorInfo<FourBy3State, FourBy3Action> *> >  si;
  vector<list<PredecessorInfo<FourBy3State, FourBy3Action> *> >	pi;
};

#endif
