import java.util.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
// this is the variant with less bugs ;-)

// this represents the possible requests
class Req
{
	final static int NONE = 0;
	final static int REQUEST = 1;
	final static int PRIO = 2;
	final static int FORK = 3; /* >= FORK is a FORK with an addition */
	final static int FORKREQ = 4;
	final static int FORKPRIO = 5;
	static String to_s(int x)
	{
		switch(x)
		{
			case NONE:	return "-%-";
			case REQUEST:	return "REQ";
			case PRIO:	return "PRIO";
			case FORK:	return "FORK";
			case FORKREQ:	return "FORK(REQ)";
			case FORKPRIO:	return "FORK(PRIO)";
			default:	return "???";
		}
	}
}

// this is a return type representing a pseudo SEND operation
// to the left or right neighbor
// This may sound a bit weird...
class Ret
{
	public int left=Req.NONE;
	public int right=Req.NONE;
	// set/get: lr is even ->  left, odd -> right neighbor
	public void set(int lr,int req)
	{
		if(lr%2==0)
			left=req;
		else
			right=req;
	}
	public int get(int lr)
	{
		if(lr%2==0)
			return left;
		else
			return right;
	}
} // indeed ;-)

class Philosoph
{
	private boolean hungry = false;
	private boolean[] holdsfork = {false,false}; // index 0 is left, 1 is right
	private boolean[] holdsprio = {false,false};
	private boolean[] needsfork = {false,false};
	// all are initialized hungry and
	// holding the right fork

	/*Philosoph(boolean fleft, boolean fright)//, boolean pleft, boolean pright)
	{
		holdsfork[0] = fleft;
		holdsfork[1] = fright;
		//holdsprio[0] = pleft;
		//holdsprio[1] = pright;
	}*/
	public boolean ishungry()
	{
		return hungry;
	}
	public void setfork(int lr)
	{
		holdsfork[lr%2]=true;
	}
	public boolean getfork(int lr)
	{
		return holdsfork[lr%2];
	}
	public void setprio(int lr)
	{
		holdsprio[lr%2]=true;
	}
	public boolean getprio(int lr)
	{
		return holdsprio[lr%2];
	}
	private void say(String x)
	{
		System.out.println("   * \""+x+"\"");
	}
	
	public Ret feelhungry() // is msg=nil I/A pair
	{
		Ret r = new Ret();
		hungry = true;
		for(int i = 0; i < 2; i++) // neighbors
		{
			if(!holdsfork[i]) 
				r.set(i, Req.REQUEST);
		}
		// FIXED: eat if possible
		if(holdsfork[0] && holdsfork[1])
		{
			say("I can eat!");
			hungry = false;
			for(int j = 0; j < 2; j++) // left and right neighbor
			{
				if(holdsprio[j])
				{
					holdsprio[j] = false;
					if(needsfork[j])
					{
						say("I'm sending FORK and PRIO "+j+" to neighbor.");
						needsfork[j] = false;
						holdsfork[j] = false;
						r.set(j, Req.FORKPRIO);
					} else {
						say("I'm sending PRIO "+j+" to neighbor.");
						r.set(j, Req.PRIO);
					}
				}
			}
		}
		return r;
	}
	public Ret request(int lr) // msg=request I/A, origin = lr
	{
		Ret r = new Ret();
		if(!hungry || !holdsprio[lr%2])
		{
			holdsfork[lr%2] = false;
			say("I'm releasing fork "+lr%2+". Neighbor can have it.");
			if(!hungry) {

				r.set(lr, Req.FORK);
			} else {
				say("But I'm sending a request.");
				r.set(lr, Req.FORKREQ);
			}
		} else
			needsfork[lr%2] = true;
		return r;
	}
	public Ret prio(int lr) // msg=priotoken I/A
	{
		Ret r = new Ret();
		holdsprio[lr%2] = true;
		return r;
	}
	public Ret fork(int lr, int m)
	{
		Ret r = new Ret();
		holdsfork[lr%2] = true;
		if(m == Req.FORKPRIO)
			holdsprio[lr%2] = true;
		if(m == Req.FORKREQ)
			needsfork[lr%2] = true;
		if(holdsfork[0] && holdsfork[1])
		{
			if(hungry=true) // <- FIXME (fixed) Sonst macht essen keinen Sinn!
				// eat!
				say("I can eat!");
			hungry = false;
			for(int j = 0; j < 2; j++) // left and right neighbor
			{
				if(holdsprio[j])
				{
					holdsprio[j] = false;
					if(needsfork[j])
					{
						say("I'm sending FORK and PRIO "+j+" to neighbor.");
						needsfork[j] = false;
						holdsfork[j] = false;
						r.set(j, Req.FORKPRIO);
					} else {
						say("I'm sending PRIO "+j+" to neighbor.");
						r.set(j, Req.PRIO);
					}
				}
			}
		}
		return r;
	}

	// output functions
	public void output()
	{
		if(holdsfork[0])
		{
			System.out.print("-");
			if(needsfork[0])
				System.out.print("*");
			else
				System.out.print("-");
		}
		else
		{
			System.out.print(" ");
			if(needsfork[0])
				System.out.print("*");
			else
				System.out.print(" ");
		}
		System.out.print("o");
		if(holdsfork[1])
		{
			if(needsfork[1])
				System.out.print("*");
			else
				System.out.print("-");
			System.out.print("-");
		} else {
			if(needsfork[1])
				System.out.print("*");
			else
				System.out.print(" ");
			System.out.print(" ");
		}
	}
	public void prioutput()
	{
		if(holdsprio[0])
			System.out.print(" $");
		else
			System.out.print("  ");
		if(hungry)
			System.out.print(".");
		else
			System.out.print(" ");
		if(holdsprio[1])
			System.out.print("$ ");
		else
			System.out.print("  ");
	}
}

class Test
{
	static int num = 1;
	public static int prev(int i)
	{
		if(i==0)
			return num-1;
		return i-1;
	}
	public static int next(int i)
	{
		if(i==num-1)
			return 0;
		return i+1;
	}
	
	public static void output(Philosoph[] c)
	{
		for(int i = 0; i < c.length; i++)
		{
			System.out.print("|");
			c[i].output();

		}
		System.out.println();
		for(int i = 0; i < c.length; i++)
		{
			System.out.print("\"");
			c[i].prioutput();
		}
		System.out.println();
		System.out.println();
	}

	public static Ret[] sendReq(LinkedList[] r, Philosoph[] c, int i)
	{
		Ret[] newreq = new Ret[2]; // return value
		if(r[i].size() > 0)
		{
			Ret req = (Ret)r[i].removeFirst();
			for(int j = 0; j < 2; j++) // left and right neighbor
			{
				if(req.get(j) == Req.NONE)
				{
					newreq[j] = new Ret(); // NONE,NONE
					continue;
				}
				int t; // prev/next
				if(j==0)
					t = prev(i);
				else
					t = next(i);
				System.out.println("c_"+t+" receives "+Req.to_s(req.get(j))+" from c_"+i);

				switch(req.get(j))
				{
					case Req.REQUEST:
						newreq[j] = c[t].request(j+1);
						break;
					case Req.PRIO:
						newreq[j] = c[t].prio(j+1);
						break;
					case Req.FORK:
						newreq[j] = c[t].fork(j+1,Req.FORK);
						break;
					case Req.FORKREQ:
						newreq[j] = c[t].fork(j+1,Req.FORKREQ);
						break;
					case Req.FORKPRIO:
						newreq[j] = c[t].fork(j+1,Req.FORKPRIO);
						break;
				}
			}
		} else {
			for(int j = 0; j < 2; j++)
				newreq[j] = new Ret();
		}
		return newreq;
	}

	public static void main(String args[]) throws java.io.IOException
	{
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		String input = null;

		System.out.println("How many philosophers?");
		System.out.print("n = ");
		input = in.readLine();
		num = Integer.parseInt(input);
		
		Philosoph[] c = new Philosoph[num];
		LinkedList[] r = new LinkedList[num];
		for(int i = 0; i < num; i++)
		{
			/* init send buffer lists (r) and philosophs (c) */
			r[i] = new LinkedList();
			c[i] = new Philosoph();
		}
		System.out.println("Setting all hungry. Here they are:");
		output(c);
		System.out.println("Please assign the forks.");
		for(int f = 0; f < num; f++)
		{
			int z;
			System.out.print("  Who gets fork "+f+"? (0 = left, 1 = right) >> ");
			z = Integer.parseInt(in.readLine())%2;
			if(z == 0)
				c[prev(f)].setfork(1);
			else
				c[f].setfork(0);
			output(c);
		}
		System.out.println("Please assign the priority tokens.");
		for(int f = 0; f < num; f++)
		{
			int z;
			System.out.print("  Who gets prio "+f+"? (0 = left, 1 = right) >> ");
			z = Integer.parseInt(in.readLine())%2;
			if(z == 0)
				c[prev(f)].setprio(1);
			else
				c[f].setprio(0);
			output(c);
		}
		System.out.println("Send hungry event to all to gain conflict? (Y/n) >> ");
		if(!in.readLine().equals("n"))
			for(int i = 0; i < num; i++)
			{
				/* send hungry event to all to gain conflict! */
				Ret t = c[i].feelhungry();
				if((t.left != Req.NONE) || (t.right != Req.NONE))
					r[i].add(t);
			}
		output(c);

		while(true)
		{
			Ret[][] reqs = new Ret[num][];
			/* now *send* the first requests of the buffers to
			 * the philosophs */
			for(int i = 0; i < num; i++)
			{
				reqs[i] = sendReq(r, c, i);
				System.out.println();
				output(c);
			}
			/* renew send/recv buffers */
			for(int i = 0; i < num; i++)
			{
				for(int j=0; j<2; j++)
				{
					int t; // prev/next
					if(j==0)
						t = prev(i);
					else
						t = next(i);
					if((reqs[i][j].left != Req.NONE) || (reqs[i][j].right != Req.NONE))
						r[t].add(reqs[i][j]);
				}
			}
			do {
				System.out.print(">>> ");
				input = in.readLine();
				if(input.length()>0)
				{
					if(input.equals("q"))
						System.exit(0);
					else
					{
						try
						{
							int z = Integer.parseInt(input)%num;
							if(!c[z].ishungry())
							{
								System.out.println("c_"+z+" is feeling hungry.");
								Ret t = c[z].feelhungry();
								if((t.left != Req.NONE) || (t.right != Req.NONE))
								r[z].add(t);
							} else
								System.out.println("c_"+z+" is already hungry.");

						} catch(NumberFormatException e)
						{
							System.out.println("You can type:");
							System.out.println(" - Return for the next tic.");
							System.out.println(" - A number i to make c_i hungry.");
							System.out.println(" - 'q' to quit.");
						}
					}
				}
			} while(input.length()>0);
		}

	}
}
