// Alex Mikhaylov 
// May 5, 1999
// CS 627 - Artificial Intelligence

#include <iostream.h>
#include <stdlib.h>

enum op_code {two_m, two_c, one_m, one_c, one_m_one_c, any}; //list of possible operations
enum boat_pos {left=0,right=1};	//list of possible boat positions

class State
{
  private:
	boat_pos Boat;	//boat position
	int MisLeft;	//how many missioners on the left bank
	int CanLeft;	//how many cannibals on the left bank
	int MisRight;	//how many missioners on the right bank
	int CanRight;	//how many cannibals on the right bank
	op_code operation;	//what operation has been performed to achieve this state. This helps
						//avoid cases when same operation gets performed twice in a row
						//(like when 2 miss. are moved left and then immediately right)
	State* next;

  public:
	State();			//constructor
	bool IsValid();		//Checks whether state is valid according to the given rules
	void PrintState();	//prints out state
	void Initialize();	//Makes state the initial one: MR=3;ML=0;CR=3;CL=0;B=right
	bool IsGoal();		//Checks whether it is the Goal state
	State* GetChildQueue();	//Returns a pointer to the head of the queue of children of this state
	State* GetTail();//Returns pointer to the tail of the queue of which this state is the head
	void SearchDeep();//performs depth-search algorithm on the tree the root of which is this state
	void SearchWide();//performs width-search algorithm on the tree the root of which is this state
};



int NumStates=0;	//keeps track of the # of states searched
int Depth=0;		//keeps track of depth level for deep-search algorithm
bool Found=false;


State::State()
{	//Default Constructor
	Boat=left;
	MisLeft=0;
	MisRight=0;
	CanLeft=0;
	next=NULL;
	CanRight=0;
}

void State::Initialize()
{
	Boat=right;
	MisLeft=0;
	MisRight=3;
	CanLeft=0;
	CanRight=3;
	operation=any;
	next=NULL;
}


bool State::IsValid()
{	//Check if # of missioners>=0, number of cannibals>=0,
	//and missioners>=cannibals
	if (MisRight==0) 
	{	
		if ((MisLeft>=CanLeft) && (MisLeft>=0) &&
		    (CanRight>=0) && (CanLeft>=0))
			return (true);
		else
			return (false);
	}
	else
	{
		if (MisLeft==0) 
		{	
			if ((MisRight>=CanRight) && (MisRight>=0) && 
				(CanRight>=0) && (CanLeft>=0))
				return (true);
			else
				return (false);
		}
		else
		{
			if ((MisRight>=CanRight) && (MisLeft>=CanLeft) &&
				(MisRight>=0) && (MisLeft>=0) &&
				(CanRight>=0) && (CanLeft>=0))
				return (true);
			else
				return (false);
		}
	}

}

void State::PrintState()
{	//Print out this state
	cout<<"ML:"<<MisLeft<<" MR:"<<MisRight<<
		" CL:"<<CanLeft<<" CR:"<<CanRight<<
		" B:"<<Boat<<endl;
}


bool State::IsGoal()
{	//Checks whether it is a goal state
	//(whether ML=3, MR=0, CL=3, CR=0, B=left)
	if ((MisLeft==3) && (CanLeft==3)
		&& (MisRight==0) && (CanRight==0)
		&& (Boat==left))
		{
			return (true);
		}
	else
			return (false);
}

State* State::GetChildQueue()
{
	State* TempChild_two_m;	
	State* TempChild_two_c;
	State* TempChild_one_m;
	State* TempChild_one_c;
	State* TempChild_one_m_one_c;
	State* Head;	//pointer to the head of the child queue to be formed
	State* temptr;	//pointer to move along child queue
	TempChild_two_m = new State;
	TempChild_two_c = new State;
	TempChild_one_m = new State;
	TempChild_one_c = new State;
	TempChild_one_m_one_c = new State;
	Head=new State;
	temptr=new State;
	
	Head=NULL;
	temptr=NULL;

	
	if (Boat==right)
	{	//boat is on the right bank
		
		//try to transport 2 missioners
		TempChild_two_m->Boat=left;
		TempChild_two_m->MisRight=MisRight-2;
		TempChild_two_m->MisLeft=MisLeft+2;
		TempChild_two_m->CanRight=CanRight;
		TempChild_two_m->CanLeft=CanLeft;
		if (TempChild_two_m->IsValid() && operation!=two_m)
		{	//the generated child is valid and is not the result of the same operation as parent
			TempChild_two_m->next=NULL;
			TempChild_two_m->operation=two_m;
			if (Head==NULL)
			{	//it is the first child in the queue, make it head	
				Head=TempChild_two_m;
				temptr=Head;
			}
			else
			{	//attach this child to the last child in the queue
				temptr->next=TempChild_two_m;
				temptr=TempChild_two_m;
			}
		}
		else
		{	//the generated child is either not valid or is the result of the same operation 
			//as parent
			TempChild_two_m=NULL;
			delete TempChild_two_m;
		}
	
		//try to transport 2 cannibals
		TempChild_two_c->Boat=left;
		TempChild_two_c->MisRight=MisRight;
		TempChild_two_c->MisLeft=MisLeft;
		TempChild_two_c->CanRight=CanRight-2;
		TempChild_two_c->CanLeft=CanLeft+2;
		if ((TempChild_two_c->IsValid()) && (operation!=two_c))
		{
			TempChild_two_c->next=NULL;
			TempChild_two_c->operation=two_c;
			if (Head==NULL)
			{				
				Head=TempChild_two_c;
				temptr=Head;
			}
			else
			{
				temptr->next=TempChild_two_c;
				temptr=TempChild_two_c;
			}
		}
		else
		{
			TempChild_two_c=NULL;
			delete TempChild_two_c;
		}
		
		//try to transport 1 missioner
		TempChild_one_m->Boat=left;
		TempChild_one_m->MisRight=MisRight-1;
		TempChild_one_m->MisLeft=MisLeft+1;
		TempChild_one_m->CanRight=CanRight;
		TempChild_one_m->CanLeft=CanLeft;
		if ((TempChild_one_m->IsValid()) && (operation!=one_m))
		{
			TempChild_one_m->next=NULL;
			TempChild_one_m->operation=one_m;
			if (Head==NULL)
			{				
				Head=TempChild_one_m;
				temptr=Head;
			}
			else
			{
				temptr->next=TempChild_one_m;
				temptr=TempChild_one_m;
			}
		}
		else
		{
			TempChild_one_m=NULL;
			delete TempChild_one_m;
		}

		//try to transport 1 cannibal
		TempChild_one_c->Boat=left;
		TempChild_one_c->MisRight=MisRight;
		TempChild_one_c->MisLeft=MisLeft;
		TempChild_one_c->CanRight=CanRight-1;
		TempChild_one_c->CanLeft=CanLeft+1;
		if ((TempChild_one_c->IsValid()) && (operation!=one_c))
		{
			TempChild_one_c->next=NULL;
			TempChild_one_c->operation=one_c;
			if (Head==NULL)
			{				
				Head=TempChild_one_c;
				temptr=Head;
			}
			else
			{
				temptr->next=TempChild_one_c;
				temptr=TempChild_one_c;
			}
		}
		else
		{
			TempChild_one_c=NULL;
			delete TempChild_one_c;
		}
		
		//try to transport 1 missioner and 1 cannibal
		TempChild_one_m_one_c->Boat=left;
		TempChild_one_m_one_c->MisRight=MisRight-1;
		TempChild_one_m_one_c->MisLeft=MisLeft+1;
		TempChild_one_m_one_c->CanRight=CanRight-1;
		TempChild_one_m_one_c->CanLeft=CanLeft+1;
		if ((TempChild_one_m_one_c->IsValid()) && (operation!=one_m_one_c))
		{
			TempChild_one_m_one_c->next=NULL;
			TempChild_one_m_one_c->operation=one_m_one_c;
			if (Head==NULL)
			{				
				Head=TempChild_one_m_one_c;
				temptr=Head;
			}
			else
			{
				temptr->next=TempChild_one_m_one_c;
				temptr=TempChild_one_m_one_c;
			}
		}
		else
		{
			TempChild_one_m_one_c=NULL;
			delete TempChild_one_m_one_c;
		}
	}
	else
	{//boat is on the left bank

		//try to transport 2 missioners
		TempChild_two_m->Boat=right;
		TempChild_two_m->MisRight=MisRight+2;
		TempChild_two_m->MisLeft=MisLeft-2;
		TempChild_two_m->CanRight=CanRight;
		TempChild_two_m->CanLeft=CanLeft;
		if ((TempChild_two_m->IsValid()) && (operation!=two_m))
		{
			TempChild_two_m->next=NULL;
			TempChild_two_m->operation=two_m;
			if (Head==NULL)
			{				
				Head=TempChild_two_m;
				temptr=Head;
			}
			else
			{
				temptr->next=TempChild_two_m;
				temptr=TempChild_two_m;
			}
		}
		else
		{
			TempChild_two_m=NULL;
			delete TempChild_two_m;
		}

		//try to transport 2 cannibals
		TempChild_two_c->Boat=right;
		TempChild_two_c->MisRight=MisRight;
		TempChild_two_c->MisLeft=MisLeft;
		TempChild_two_c->CanRight=CanRight+2;
		TempChild_two_c->CanLeft=CanLeft-2;
		if ((TempChild_two_c->IsValid()) && (operation!=two_c))
		{
			TempChild_two_c->next=NULL;
			TempChild_two_c->operation=two_c;
			if (Head==NULL)
			{				
				Head=TempChild_two_c;
				temptr=Head;
			}
			else
			{
				temptr->next=TempChild_two_c;
				temptr=TempChild_two_c;
			}
		}
		else
		{
			TempChild_two_c=NULL;
			delete TempChild_two_c;
		}
		
		//try to transport 1 missioner
		TempChild_one_m->Boat=right;
		TempChild_one_m->MisRight=MisRight+1;
		TempChild_one_m->MisLeft=MisLeft-1;
		TempChild_one_m->CanRight=CanRight;
		TempChild_one_m->CanLeft=CanLeft;
		if ((TempChild_one_m->IsValid()) && (operation!=one_m))
		{
			TempChild_one_m->next=NULL;
			TempChild_one_m->operation=one_m;
			if (Head==NULL)
			{				
				Head=TempChild_one_m;
				temptr=Head;
			}
			else
			{
				temptr->next=TempChild_one_m;
				temptr=TempChild_one_m;
			}
		}
		else
		{
			TempChild_one_m=NULL;
			delete TempChild_one_m;
		}

		//try to transport 1 cannibal
		TempChild_one_c->Boat=right;
		TempChild_one_c->MisRight=MisRight;
		TempChild_one_c->MisLeft=MisLeft;
		TempChild_one_c->CanRight=CanRight+1;
		TempChild_one_c->CanLeft=CanLeft-1;
		if ((TempChild_one_c->IsValid()) && (operation!=one_c))
		{
			TempChild_one_c->next=NULL;
			TempChild_one_c->operation=one_c;
			if (Head==NULL)
			{				
				Head=TempChild_one_c;
				temptr=Head;
			}
			else
			{
				temptr->next=TempChild_one_c;
				temptr=TempChild_one_c;
			}
		}
		else
		{
			TempChild_one_c=NULL;
			delete TempChild_one_c;
		}

		//try to transport 1 missioner and 1 cannibal
		TempChild_one_m_one_c->Boat=right;
		TempChild_one_m_one_c->MisRight=MisRight+1;
		TempChild_one_m_one_c->MisLeft=MisLeft-1;
		TempChild_one_m_one_c->CanRight=CanRight+1;
		TempChild_one_m_one_c->CanLeft=CanLeft-1;
		if ((TempChild_one_m_one_c->IsValid()) && (operation!=one_m_one_c))
		{
			TempChild_one_m_one_c->next=NULL;
			TempChild_one_m_one_c->operation=one_m_one_c;
			if (Head==NULL)
			{				
				Head=TempChild_one_m_one_c;
				temptr=Head;
			}
			else
			{
				temptr->next=TempChild_one_m_one_c;
				temptr=TempChild_one_m_one_c;
			}
		}
		else
		{
			TempChild_one_m_one_c=NULL;
			delete TempChild_one_m_one_c;
		}
	}
	//return pointer to the head of the child queue
	return (Head);
}


void State::SearchDeep()
{//deep search algorithm
	State* ChildQueuePtr;
	ChildQueuePtr=new State;
	ChildQueuePtr=NULL;

	if (Found)	//check whether recursion has to be stopped
		return;

	NumStates++;
	
	PrintState();
	if (!IsGoal())		//if this is not goal state
	{
		ChildQueuePtr=GetChildQueue();	//get child queue for this state
		while (!ChildQueuePtr==NULL)
		{
			ChildQueuePtr->SearchDeep(); //perform recursive deep search on the head 
										 //of the child queue
			ChildQueuePtr=ChildQueuePtr->next; //move along child queue
		}
		return;
	}
	else
	{
		cout<<"FOUND!!!"<<endl;
		cout<<"Went through "<<NumStates<<" states"<<endl;
		Found=true;	//set Found=true to stop recursion
		return;
	}
}

State* State::GetTail()
{//get tail of the queue referenced by this state
	State* temptr;
	temptr=new State;
	temptr=this;
	while (!temptr->next==NULL)
		temptr=temptr->next;
	return (temptr);
}
 
void State::SearchWide()
{	/*wide search algorithm
	  this function first checks all states at the certain depth, then, if it does not 
	  come across the goal state, gets children for all the states at that depth,
	  links all the children into one queue and passes that queue back to this function 
	  for recursion*/
	State* ChildQuePtr;
	State* temptr;
	State* ChildHead;
	State* NextChild;

	ChildQuePtr=new State;
	temptr=new State;
	ChildHead=new State;
	NextChild=new State;

	ChildQuePtr=NULL;
	ChildHead=NULL;
	NextChild=NULL;

	if(Found)	//stop recursion if goal state is found
		return;
	
	temptr=this;
	Depth++;
	
	//go through all states at the same depth as the one for which the function is called 
	//and check if one of them is goal state
	while (!temptr==NULL)
	{
		cout<<" Depth: "<<Depth<<"   ";
		PrintState();
	
		NumStates++;
		if (temptr->IsGoal())
		{
			cout<<"FOUND!!!"<<endl;
			cout<<"Went through "<<NumStates<<" states"<<endl;
			Found=true;
			return;
		}
		else
			temptr=temptr->next;
	}

	temptr=this;

	//find first available child of the states in the queue
	while((ChildHead==NULL) && (temptr!=NULL))
	{
		ChildHead=temptr->GetChildQueue();
		temptr=temptr->next;
	}
	if (ChildHead==NULL)
		return; //no children found at this depth
	else
	{
		//form a queue of all children at this depth
		ChildQuePtr=ChildHead;
		while (!temptr==NULL)
		{
			NextChild=temptr->GetChildQueue();
			if (!NextChild==NULL)
			{
				ChildQuePtr->GetTail()->next=NextChild;
			}
			temptr=temptr->next;
		}
		//pass the head of the children's queue back for recursion
		ChildHead->SearchWide();
	}

}

void main()
{
	//create initial state
	State InitState;
	InitState.Initialize();
	//initialize global variables
	Found=false;
	NumStates=0;
	cout<<"Searching Deep"<<endl<<endl;
	//perform deep search
	InitState.SearchDeep();
	
	//re-initialize global variables
	Found=false;
	Depth=0;
	NumStates=0;
	cout<<"Searching Wide"<<endl<<endl;
	//perform wide search
	InitState.SearchWide();
}






