package guohe;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * 狼羊过河问题
 * @author tiger
 * @date 2011年1月27日。
 * 
 * 这里是用面向对象的编程方式,有定义了狼和羊两个类。
 * 但其实程序里并没有用到它们的行为,该问题只是关心它们的数目而已!
 * 所以其实可以直接就定义四个变量来存储两岸的狼数和羊数,然后在遍历
 * 递归的同时增减这四个数字即可。
 * 
 */
public class Guohe {

	//左岸的狼数和羊数
	private List<Actor> leftLangs = new LinkedList<Actor>();
	private List<Actor> leftYangs = new LinkedList<Actor>();
	
	//右岸的狼数和羊数
	private List<Actor> rightLangs = new LinkedList<Actor>();
	private List<Actor> rightYangs = new LinkedList<Actor>();
	
	//存储所有成功过河的方案
	private List winSteps = new ArrayList();
	
	public Guohe() {
		init();
	}
	
	/**
	 * 刷新重置盘面为初始情况
	 */
	private void init() {
		leftLangs.clear();
		leftYangs.clear();
		rightLangs.clear();
		rightYangs.clear();
		for (int i = 0; i < 3; i++) {
			leftLangs.add(new Lang());
			leftYangs.add(new Yang());
		}
		
		//这两个状态需要初始放进去。
		states.add("3,3,0,0,1");
		states.add("3,3,0,0,2");
		
		//方向设为初始向对岸的方向
		fangxiang = TYPE_FANGXIANG_GO;
	}
	
	
	//到对岸:go , 回来:come 
	private static int TYPE_FANGXIANG_GO = 1;
	private static int TYPE_FANGXIANG_COME = 2;
	//船行方向
	private int fangxiang = TYPE_FANGXIANG_GO;
	
	/**
	 * 保存盘面的状态
	 * 
	 * 保存的是有5个数字的字符串,这5个数字代表的意义依次是:
	 * 左岸狼数、左岸羊数、右岸狼数、右岸羊数、当前船行方向。
	 * 
	 * 状态的保存非常重要,把所有递归遍历遇到的状态都保存在这里,
	 * 当后面的某次遍历所遇到的盘面已经有在states里存在的话,说明该
	 * 盘面已经被处理过,则可以直接跳过。
	 * 
	 * 保存下已经处理过的盘面,这样的设计可以防止出现死循环导致内存泄露。
	 */
	private List states = new ArrayList();
	
	
	/**
	 * 针对当前船行方向,根据当前盘面情况,即两岸的狼数和羊数
	 * 得到可以上船的狼数和羊数。 该方法中会把盘面状态保存入states.
	 * @param fangxiang
	 * @return
	 */
	private List<int[]> getChuanMember(int fangxiang)
	{
		List<int[]> rtnlist = new LinkedList<int[]>();
		String key = "";
		
		List alangs, ayangs, blangs, byangs;
		if(fangxiang == TYPE_FANGXIANG_GO)
		{
			alangs = leftLangs;
			ayangs = leftYangs;
			blangs = rightLangs;
			byangs = rightYangs;
		}else{
			alangs = rightLangs;
			ayangs = rightYangs;
			blangs = leftLangs;
			byangs = leftYangs;
		}
		
		//判断 1,0 .(1狼0羊)
		if(alangs.size() >= 1)
		{
			if((alangs.size() - 1 <= ayangs.size() || ayangs.size() == 0) && (blangs.size() + 1 <= byangs.size() || byangs.size() == 0))
			{
				if(fangxiang == TYPE_FANGXIANG_GO)
				{
					key = (alangs.size() - 1) + "," + ayangs.size() + "," + (blangs.size() + 1) + "," + byangs.size() + "," + TYPE_FANGXIANG_GO;
				}else{
					key = (blangs.size() + 1) + "," + byangs.size() + "," + (alangs.size() - 1) + "," + ayangs.size() + "," + TYPE_FANGXIANG_COME;
				}
				
				if(!states.contains(key))
				{
					rtnlist.add(new int[]{1, 0});
					states.add(key);
				}
			}
		}
		
		//判断 2,0 .(2狼0羊)
		if(alangs.size() >= 2)
		{
			if((alangs.size() - 2 <= ayangs.size() || ayangs.size() == 0) && (blangs.size() + 2 <= byangs.size() || byangs.size() == 0))
			{
				if(fangxiang == TYPE_FANGXIANG_GO)
				{
					key = (alangs.size() - 2) + "," + ayangs.size() + "," + (blangs.size() + 2) + "," + byangs.size() + "," + TYPE_FANGXIANG_GO;
				}else{
					key = (blangs.size() + 2) + "," + byangs.size() + "," + (alangs.size() - 2) + "," + ayangs.size() + "," + TYPE_FANGXIANG_COME;
				}
				
				if(!states.contains(key))
				{
					rtnlist.add(new int[]{2, 0});
					states.add(key);
				}
			}
		}
		
		//判断0,1 .(0狼1羊)
		if(ayangs.size() >= 1)
		{
			if((alangs.size() <= ayangs.size() - 1 || ayangs.size() - 1 == 0) && (blangs.size() <= byangs.size() + 1 || byangs.size() + 1 == 0))
			{
				if(fangxiang == TYPE_FANGXIANG_GO)
				{
					key = alangs.size() + "," + (ayangs.size() - 1) + "," + blangs.size() + "," + (byangs.size() + 1) + "," + TYPE_FANGXIANG_GO;
				}else{
					key = blangs.size() + "," + (byangs.size() + 1) + "," + alangs.size() + "," + (ayangs.size() - 1) + "," + TYPE_FANGXIANG_COME;
				}
				
				if(!states.contains(key))
				{
					rtnlist.add(new int[]{0, 1});
					states.add(key);
				}
			}
		}
		//判断0,2 .(0狼2羊)
		if(ayangs.size() >= 2)
		{
			if((alangs.size() <= ayangs.size() - 2 || ayangs.size() - 2 == 0) && (blangs.size() <= byangs.size() + 2 || byangs.size() + 2 == 0))
			{
				if(fangxiang == TYPE_FANGXIANG_GO)
				{
					key = alangs.size() + "," + (ayangs.size() - 2) + "," + blangs.size() + "," + (byangs.size() + 2) + "," + TYPE_FANGXIANG_GO;
				}else{
					key = blangs.size() + "," + (byangs.size() + 2) + "," + alangs.size() + "," + (ayangs.size() - 2) + "," + TYPE_FANGXIANG_COME;
				}
				
				if(!states.contains(key))
				{
					rtnlist.add(new int[]{0, 2});
					states.add(key);
				}
			}
		}
		
		//判断1,1 .(1狼1羊)
		if(alangs.size() >= 1 && ayangs.size() >= 1)
		{
			if((alangs.size() - 1 <= ayangs.size() - 1 || ayangs.size() - 1 == 0) && (blangs.size() + 1 <= byangs.size() + 1 || byangs.size() + 1 == 0))
			{
				if(fangxiang == TYPE_FANGXIANG_GO)
				{
					key = (alangs.size() - 1) + "," + (ayangs.size() - 1) + "," + (blangs.size() + 1) + "," + (byangs.size() + 1) + "," + TYPE_FANGXIANG_GO;
				}else{
					key = (blangs.size() + 1) + "," + (byangs.size() + 1) + "," + (alangs.size() - 1) + "," + (ayangs.size() - 1) + "," + TYPE_FANGXIANG_COME;
				}
				
				if(!states.contains(key))
				{
					rtnlist.add(new int[]{1, 1});
					states.add(key);
				}
			}
		}
		return rtnlist;
	}
	
	/**
	 * 用来存储当前遍历的方案步骤
	 * 
	 * 存储的是大小为3的数字数组。各元素的意义依次是:
	 * 船行方向、船上狼数、船上羊数
	 * 
	 */
	private List<int[]> temp = new ArrayList<int[]>();
	
	/**
	 * 递归方法,不断深度遍历
	 */
	private void digui()
	{
		//得到当前盘面下,可以上船的狼数和羊数
		List list = this.getChuanMember(fangxiang);
		
		//如果找不到可乘船的狼羊,则说明当前的盘面无法走向成功,则退出。
		if(list.isEmpty())
		{
			//重置为初始盘面情况
			init(); 
			//清空之前的步骤存储
			temp.clear();
			return;
		}
		for (int i = 0; i < list.size(); i++) {
			int[] element = (int[]) list.get(i);
			
			if(fangxiang == TYPE_FANGXIANG_GO)
			{				
				//修改两岸的狼数
				if(element[0] == 1)
				{					
					Actor a = this.leftLangs.remove(0);
					this.rightLangs.add(a);
				}
				else if(element[0] == 2)
				{
					Actor a = this.leftLangs.remove(0);
					this.rightLangs.add(a);
					a = this.leftLangs.remove(0);
					this.rightLangs.add(a);
				}
				//修改两岸的羊数
				if(element[1] == 1)
				{					
					Actor a = this.leftYangs.remove(0);
					this.rightYangs.add(a);
				}
				else if(element[1] == 2)
				{
					Actor a = this.leftYangs.remove(0);
					this.rightYangs.add(a);
					a = this.leftYangs.remove(0);
					this.rightYangs.add(a);
				}
				
				//存储步骤
				temp.add(new int[]{fangxiang, element[0], element[1]});
				//判断是否完成,如果完成,将完成步骤加入完成方案的列表里
				if(this.success())
				{
					//打印成功方案
					System.out.println("方案:(共" + temp.size() + "步)");
					for (int j = 0; j < temp.size(); j++) {
						int[] a = (int[]) temp.get(j);
						System.out.println("第" + (j + 1) + "步, " + (a[0] == 1 ? "前进" : "后退") + "---> 狼" + a[1] + ",羊" + a[2]);
					}
					winSteps.add(temp);
					
					//重置盘面为初始情况
					init();
					//清空之前的步骤存储
					temp.clear();
					return; //这里是return而非continue,是因为既然已经成功了,而当前是最后一步,那么后面的循环必然不能成功。--->不大肯定。
				}
				fangxiang = TYPE_FANGXIANG_COME;
				digui();
			}
			else if(fangxiang == TYPE_FANGXIANG_COME)
			{				
				//修改两岸的狼数
				if(element[0] == 1)
				{					
					Actor a = this.rightLangs.remove(0);
					this.leftLangs.add(a);
				}
				else if(element[0] == 2)
				{
					Actor a = this.rightLangs.remove(0);
					this.leftLangs.add(a);
					a = this.rightLangs.remove(0);
					this.leftLangs.add(a);
				}
				//修改两岸的羊数
				if(element[1] == 1)
				{					
					Actor a = this.rightYangs.remove(0);
					this.leftYangs.add(a);
				}
				else if(element[1] == 2){
					Actor a = this.rightYangs.remove(0);
					this.leftYangs.add(a);
					a = this.rightYangs.remove(0);
					this.leftYangs.add(a);
				}
				//存储步骤
				temp.add(new int[]{fangxiang, element[0], element[1]});
				//调换方向
				fangxiang = TYPE_FANGXIANG_GO;
				//递归处理现在的盘面
				digui();
			}
		}
	}
	
	
	private boolean success()
	{
		return this.leftLangs.isEmpty() && this.leftYangs.isEmpty() && this.rightLangs.size() == 3 && this.rightYangs.size() == 3;
	}
	
	
	
	public static final int TYPE_LANG = 1;
	public static final int TYPE_YANG = 2;
	class Actor {
		public int type = 0;
		public boolean isLang()
		{
			return type == TYPE_LANG;
		}
		public boolean isYang()
		{
			return type == TYPE_YANG;
		}
	}
	//狼
	class Lang extends Actor{
		public Lang() {
			type = TYPE_LANG;
		}
	}
	//羊
	class Yang extends Actor{
		public Yang() {
			type = TYPE_YANG;
		}
	}
	
	public static void main(String[] args) {
		new Guohe().digui();
	}
	
}