UVA

/*
  法一:
	
  之前不会象棋的玩法,为了这题特意百度了许久,弄清楚了基本规则
  
  另,这题一开始的代码十分繁琐,后来看到了一篇很好的博客,他的思路就很简洁:
  http://blog.csdn.net/hao_zong_yin/article/details/53780164
  
  于是,我也回去修改了自己的代码,才有了这个最终的版本
  
  另外总结一下,上面那篇博客究竟好到哪里,为什么能简化代码?
  
  因为,它其实是作了一个替换,我们事先无法知道红子的个数,需要等n去输入,而我们却清楚地知道,黑子只有一个...
  
  这样的情况下,我们先模拟完黑子走的那步(如果当时是"飞将"的情况,黑子走完这步就能胜利,直接结束游戏,不出现checkmate)以后,其实可以有两种思路:
  1.枚举黑将可走的位置,判断是否有不会被红子下轮攻击到的位置,没有就被将死
  2.找到所有红子可将军的所有可能位置,再找黑将可走的位置中,有没有能幸免于难的,如果一个都没有,就被将死了
  
  总体来说,我觉得1会更加简单
  
  但是现在的问题就是,真的要一步步去模拟所有红子的位置吗?
  上面的链接的博主就转换了思路,把红子移动仍然当成是黑子移动(运动是相对的,黑子下移就表示了红子上移),黑子向整个棋盘(注意!!此时我们是用黑子的移动代替红子,黑子不是只能在九宫里走了,而是和红子的移动范围一样,是整个棋盘,这里尤其注意!!!)4个方向移动,找是否存在能够将死它的红子,这样就只用枚举4个方向,会简单许多
  
  因为,如果想要找到红子的所有攻击区域,每个红子都要枚举方向,还要考虑不同的攻击规则
  
  因此,不如就把红子的移动用黑子的代替了,去找有没有能将死它的红子即可
  
  突然觉得,上面的哪个博主的思路真厉害,用了转换以后,感觉代码也简单许多了。也感受到,同一道题目,用不同的角度,简直就是有新的发现,有一种豁然开朗的感觉...看来每道题都值得挖深一点,果然做完题还该时刻反省:这是不是我能想到的最好的做法了?还可以有别的思路吗?
  
  BTW,注释是写给和我一样对象棋十分小白的人,以防我下次复习这道题时,又忘了象棋的规则 T^T...熟悉规则的就,让你见笑了...
*/



#include <iostream>
#include <cstring>
#include <string>
using namespace std;
char chessboard[20][20];
int IsCheckmate(int x, int y);
int IsAlive(int x, int y);

int main()
{
	int n, x, y, x1, y1;
	char t; //type
	while (cin >> n >> x >> y && n && x && y)
	{
		memset(chessboard, 0, sizeof(chessboard)); //清空上一组数据的记录 
		for (int i = 0; i < n; i++)
		{
			cin >> t >> x1 >> y1;
			chessboard[x1][y1] = t;  //放子 
		}
		
		if (IsCheckmate(x, y)) cout << "YES" << endl;
		else cout << "NO" << endl;
	} 

	return 0;
}

int IsCheckmate(int x, int y)
{
	int i;
	char ch;
	
	for (i = x; i <= 10; i++) 
	{
		if (chessboard[i][y] != 0) break; 
	}
	if (i <= 10 && chessboard[i][y] == 'G') return 0; //初始情况下,将帅如果碰面,且满足"飞将"规则,帅死,不会出现 checkmate 的局面 
	
	//枚举四个方向,对每个方向,先判断移动是否越界(将能走的界限是九宫,3 * 3的一个区域),确认不越界后,如果黑将向某个方向移动,能保证在下步中可不被将死,则返回0,表示黑将不会checkmate,否则恢复原来的棋盘(类似“回溯法”的思想),继续尝试别的方向,循环往复,直到这个某个方向不被将死(返回0),或方向枚举完毕(返回1)
	 
	 if (x - 1 >= 1)
	 {
	 	ch = chessboard[x - 1][y];
	 	chessboard[x - 1][y] = 0;
	 	if ( IsAlive(x - 1, y) ) return 0;
	 	else chessboard[x - 1][y] = ch;
	 }
	 /*
	 解释下这个if分支的意义,下面的几个if同理:
	 1.if内的内容保证不越界,之所以要赋值ch,是因为黑子上移的位置,可能是空白(直接落子),也可能是红子(被黑子吃掉),不管怎么说,最后chessboard的相应位置都要清空, 因为chessboard里面记录的,都是红子的位置。黑子上移,将它的位置作为参数传给IsAlive(),判断是否有不被将死的可能
	 2.如果上移后,在红方移动后,无处可避,只能尝试别的方向,此时注意要先把 对应位置 恢复为ch,这里和回溯法的思想十分类似
	 */ 
	 
	 if (x + 1 <= 3)
	 {
	 	ch = chessboard[x + 1][y];
	 	chessboard[x + 1][y] = 0;
	 	if ( IsAlive(x + 1, y) ) return 0;
	 	else chessboard[x + 1][y] = ch;
	 }
	 
	 if (y - 1 >= 4)
	 {
	 	ch = chessboard[x][y - 1];
	 	chessboard[x][y - 1] = 0;
	 	if ( IsAlive(x, y - 1)) return 0;
	 	else chessboard[x][y - 1] = ch;
	 }
	 
	 if (y + 1 <= 6)
	 {
	 	ch = chessboard[x][y + 1];
	 	chessboard[x][y + 1] = 0;
	 	if ( IsAlive(x, y + 1)) return 0;
	 	else chessboard[x][y + 1] = ch;
	 }
	 return 1;
}

int IsAlive(int x, int y) //判断黑方如果放到(x,y)以后,红方走子后,黑方是否有逃脱不被将死的可能 
{
	//判断是否会被周围的马吃掉,此处几个if,是为了判断:1.是否在马的攻击范围内,2.是否是蹩马腿,3.是否越界
	//注意此时马的攻击范围的圈定,是红马在中心,黑将在8个可能的攻击点,再由黑将的坐标,推出可能导致自己被将死的红马位置,以及为了保证不发生蹩马腿,需要不为红子的那个位置(之前我就弄反过...看来我转换的思想还没学到位) 
	//几个 if 说明此时黑将在红马的攻击范围内,且不会被蹩马腿,则这种走法会被红马攻击,不可取,回溯,下面几种情况同理,但有时还要增加对越界的判断(这是出于对黑将初始状态的考虑,结合移动的范围,可能向上出界,但不会向下向左向右出界)
	
	if (chessboard[x + 2][y - 1] == 'H' && chessboard[x + 1][y - 1] == 0) return 0;
	if (chessboard[x + 2][y + 1] == 'H' && chessboard[x + 1][y + 1] == 0) return 0;
	if (chessboard[x + 1][y - 2] == 'H' && chessboard[x + 1][y - 1] == 0) return 0;
	if (chessboard[x + 1][y + 2] == 'H' && chessboard[x + 1][y + 1] == 0) return 0; 
	
	if (x - 2 >= 1 && chessboard[x - 2][y - 1] == 'H' && chessboard[x - 1][y - 1] == 0) return 0;
	if (x - 2 >= 1 && chessboard[x - 2][y + 1] == 'H' && chessboard[x - 1][y + 1] == 0) return 0;
	if (x - 1 >= 1 && chessboard[x - 1][y - 2] == 'H' && chessboard[x - 1][y - 1] == 0) return 0;
	if (x - 1 >= 1 && chessboard[x - 1][y + 2] == 'H' && chessboard[x - 1][y + 1] == 0) return 0; 
	
	int i, num = 0;
	// 现在开始遍历四个方向,看看黑将是否在红方的任意车、炮、将的攻击范围
	// num用来记录,目前这个红子是该方向遇到的第几个红子,注意num在每次换方向时,都要清零 
	// 注意车和炮类似,除了炮在吃子时,需要和被吃的子恰好相隔一子,而车若想*移动而不被挡住,需要红车与黑将之间,没有其他红方棋子的阻碍
	// 注意4个方向里,有个比较特殊的就是,向下找有没有棋子能攻击黑将时,要考虑"飞将"这种可能 
	// num++,以及满足特定条件时return 0的判断,仅在 有红子且 num == 1/2时,是因为,如果已经遇到了两个红子还没被吃,那就绝对不会再被吃子了,因为车和炮的吃子方式,都不是隔着两个子还能吃子的
	 
	
	for (i = x; i >= 1; i--)
	{
		if (chessboard[i][y] != 0)
		{
			num++;
			if (num == 1 && chessboard[i][y] == 'R') return 0;
			else if (num == 2 && chessboard[i][y] == 'C') return 0;
			else if (num == 2) break;
		}
	} // 上方没有能攻击它的红子 
	
	num = 0;
	for (i = x; i <= 10; i++)
	{
		if (chessboard[i][y] != 0)
		{
			num++;
			if (num == 1 && ( chessboard[i][y] == 'R' || chessboard[i][y] == 'G' )) return 0; //注意:向下可飞将 
			else if (num == 2 && chessboard[i][y] == 'C') return 0;
			else if (num == 2) break;
		}
	}
	
	num = 0;
	for (i = y; i >= 1; i--)
	{
		if (chessboard[x][i] != 0)
		{
			num++;
			if (num == 1 && chessboard[x][i] == 'R') return 0;
			else if (num == 2 && chessboard[x][i] == 'C') return 0;
			else if (num == 2) break;
		}
	}
	
	num = 0;
	for (i = y; i <= 9; i++)
	{
		if (chessboard[x][i] != 0)
		{
			num++;
			if (num == 1 && chessboard[x][i] == 'R') return 0;
			else if (num == 2 && chessboard[x][i] == 'C') return 0;
			else if (num == 2) break;
		}
	}
	return 1;	
}


/*
  法二:这种方法是先找到红子的所有可攻击范围,再找黑子可走的坐标中,有没有不在红子攻击范围内的位置
  参考了这个blog的代码,我发现这个博主写的十分简洁,于是借用了他的思路和设计了哪些函数,对我自己写的长代码做了一些改进,以下贴出自己改进后的AC代码
  
  blog: http://blog.csdn.net/shihongliang1993/article/details/73287616
  (BTW,我对题意的理解,似乎和上面的这个博主不太一样,不过最终都能AC,那应该就没什么关系了)
  

  题意:黑方只剩下一个将,红方还有很多子,而且红方已出子,轮到黑方出子,判断是否出现checkmate
  (凡走子直接攻击对方将帅者谓之“将军”(check),其中另一方无法解围的情况称为“将死”(checkmate))
  
  换句话说,就是判断黑将走了一子以后,红方再将军时,黑子能否解围,不能则被将死
  
1.找出红方能吃到的位置,记录到check数组中

2.给棋子编码,炮是1,马是2,车是3,因为在对将的时候帅的作用和车是一样的,因此把帅也编成3号,按照车处理(帅吃将其实只有一种可能,就是帅是将正下方遇到的第一个红子)

3.这中间使用两个棋盘来记录,board是残局时每个棋子的位置,check记录判断后红方能够吃到的位置,0代表不能吃到,-1表示能吃到。
若只用一个棋盘,如果一个红子被另外一个红子所吃,它被标记为别的,而非1/2/3,在后面的判断时,就会漏掉这个红子的攻击范围的标记,最终导致出错

4.边界的判断易错,因为黑将移动时可能吃掉红子
因此在判断红方棋子能吃到的位置的时候,要考虑到红方能够吃到的红方自己的第一个棋子(这是为了未雨绸缪,如果这第一个棋子被黑子吃掉了,黑子就恰在红方攻击范围内了)

  值得一提的是,这个博主的代码中,十分巧妙的一点是,他用了几个数组,来实现黑子的走子,红马的走子(马的可走位置和马腿<马腿是对应"蹩马腿"的叫法,表示某个位置被挡以后,马不能攻击对应方向的棋子>的位置,它们一一对应时的坐标变化量,都是通过数组实现的),简化了代码
  
  此外,坐标的合法性判断也是通过函数实现的,也精简了代码,值得学习
*/

#include <iostream>
#include <cstring>
using namespace std;
int board[20][20], check[20][20];
int dxy[5][2] = {{-1, 0}, {1, 0}, {0, 0}, {0, - 1}, {0, 1}};
int attack[8][2] = { {-1, 2}, {1, 2}, {-1, -2}, {1, -2}, {-2, -1}, {-2, 1}, {2, 1}, {2, -1} };
int leg[8][2] = { {0, 1}, {0, 1}, {0, -1}, {0, -1}, {-1, 0}, {-1, 0}, {1, 0}, {1, 0} };

int islegal(int x, int y)
{
	return (x >= 1 && x <= 10 && y >= 1 && y <= 9);
}

void chessH(int x, int y) // horse
{
	for (int i = 0; i < 8; i++)
	{
		int lx = x + leg[i][0], ly = y + leg[i][1];
		int hx = x + attack[i][0], hy = y + attack[i][1];
		if (islegal(lx, ly) && board[lx][ly] == 0 && islegal(hx, hy))
		check[hx][hy] = -1;
	}
}

void chessR(int x, int y) //chariot
{
	for (int i = x - 1; i >= 1; i--)
	{
		if (islegal(i, y))
		{
			check[i][y] = -1;
			if (board[i][y] != 0) break;
		}
	}
	
	for (int i = x + 1; i <= 10; i++)
	{
		if (islegal(i, y))
		{
			check[i][y] = -1;
			if (board[i][y] != 0) break;
		}
	}
	
	for (int i = y - 1; i >= 1; i--)
	{
		if (islegal(x, i))
		{
			check[x][i] = -1;
			if (board[x][i] != 0) break;
		}
	}
	
	for (int i = y + 1; i <= 9; i++ )
	{
		if (islegal(x, i))
		{
			check[x][i] = -1;
			if (board[x][i] != 0) break;
		}
	}
}

void chessC(int x, int y)// cannon
{
	int t;
	t = x - 1; while (islegal(t, y) && board[t][y] == 0) t--;
	for (int i = t - 1; i >= 1; i--)
	{
		if (islegal(i, y))
		{
			check[i][y] = -1;
			if (board[i][y] != 0) break;
		}
	}
	
	t = x + 1; while (islegal(t, y) && board[t][y] == 0) t++;
	for (int i = t + 1; i <= 10; i++)
	{
		if (islegal(i, y))
		{
			check[i][y] = -1;
			if (board[i][y] != 0) break;
		}
	}
	
	t = y - 1; while (islegal(x, t) && board[x][t] == 0) t--;
	for (int i = t - 1; i >= 1; i--)
	{
		if (islegal(x, i))
		{
			check[x][i] = -1;
			if (board[x][i] != 0) break;
		}
	}
	
	for (int i = t + 1; i <= 9; i++)
	{
		if (islegal(x, i))
		{
			check[x][i] = -1;
			if (board[x][i] != 0) break;
		}
	}
}


int main()
{
	int n, x1, y1, x, y;
	char t;
	while (cin >> n >> x1 >> y1 && n && x1 && y1)
	{
		memset(board, 0, sizeof(board));
		memset(check, 0, sizeof(check));
		
		for (int i = 0; i < n; i++)
		{
			cin >> t >> x >> y;
			switch(t)
			{
				case 'C': board[x][y] = 1; break;
				case 'H': board[x][y] = 2; break;
				case 'R': board[x][y] = 3; 
				case 'G': board[x][y] = 3; break;
			}
		}
		
		for (x = 1; x <= 10; x++)
		for (y = 1; y <= 9; y++)
		{
			if (board[x][y] > 0)
			switch(board[x][y])
			{
				case 1: chessC(x, y);break;
				case 2: chessH(x, y);break;
				case 3: chessR(x, y);break;
			}
		}
		
		int flag = 1;
		for (int i = 0; i < 5; i++)
		{
			x = x1 + dxy[i][0]; y = y1 + dxy[i][1];
			if (x >= 1 && x <= 3 && y >= 4 && y <= 6 && check[x][y] == 0)
			{
				flag = 0;
				break;
			}
		}
		
		cout << (flag ? "YES" : "NO") << endl;
	}
	return 0;
}