#include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



#define SIZE 40

enum Type{
	UNDEFINED,
	A,
	B,
	C,
};

struct Point{
	Point(double arg_x,double arg_y){
		x = arg_x;
		y = arg_y;
	}

	Point(){
		x = y = 0.0;
	}

	Point operator + (Point p){ return Point(x+p.x,y+p.y); }
	Point operator - (Point p){ return Point(x-p.x,y-p.y);}
	Point operator * (double a){ return Point(a*x,a*y); }
	Point operator / (double a){ return Point(x/a,y/a); }

	double abs(){ return sqrt(norm()); }
	double norm(){ return x*x + y*y; }

	bool operator<(const Point &p) const{
		return x != p.x? x < p.x: y < p.y;
	}

	bool operator == (const Point &p) const{
		return fabs(x-p.x) < EPS && fabs(y-p.y) < EPS;
	}

	double x,y;
};

typedef Point Vector;
typedef vector<Point> Polygon;

struct Line{
	Line(){

	}
	Line(Point a,Point b){
		p[0] = a;
		p[1] = b;
	}
	Point p[2];
};

struct Info{
	Info(int arg_node_id,ll arg_node_size){
		node_id = arg_node_id;
		node_size = arg_node_size;
	}
	bool operator<(const struct Info &arg) const{

		if(node_size != arg.node_size){

			return node_size < arg.node_size;
		}else{

			return node_id < arg.node_id;
		}
	}

	int node_id;
	ll node_size;
};

int N;
int boss[SIZE],height[SIZE],num_member[SIZE];
int count_table[SIZE];
Polygon POLYGON[SIZE];
vector<int> G[SIZE],member[SIZE];
Type color[SIZE],work[SIZE];
bool clear_FLG,visited[SIZE];

int get_boss(int id){
	if(boss[id] == id)return id;
	else{
		return boss[id] = get_boss(boss[id]);
	}
}

int isSame(int x,int y){
	return get_boss(x) == get_boss(y);
}

void unite(int x,int y){
	int boss_x = get_boss(x);
	int boss_y = get_boss(y);

	if(boss_x == boss_y)return;

	if(height[x] > height[y]){

		num_member[boss_x] += num_member[boss_y];
		boss[boss_y] = boss_x;

	}else if(height[x] < height[y]){

		num_member[boss_y] += num_member[boss_x];
		boss[boss_x] = boss_y;

	}else{ //height[x] == height[y]

		num_member[boss_x] += num_member[boss_y];
		boss[boss_y] = boss_x;
		height[x]++;
	}
}

void init(){

	for(int i = 0; i < SIZE; i++){

		POLYGON[i].clear();
		member[i].clear();
		G[i].clear();

		boss[i] = i;
		height[i] = 0;
		num_member[i] = 1;
	}
}


bool is_OK(int group_id){

	for(int i = 0; i < member[group_id].size(); i++){

		visited[member[group_id][i]] = false; //★★塗り残し領域が連結しているとは限らない★★
	}

	for(int i = 0; i < member[group_id].size(); i++){

		int start = member[group_id][i];
		if(work[start] == C || visited[start] == true)continue;

		work[start] = A;
		visited[start] = true;
		queue<int> Q;

		vector<int> TMP;

		Q.push(start);
		TMP.push_back(start);

		while(!Q.empty()){

			for(int k = 0; k < G[Q.front()].size(); k++){

				int next = G[Q.front()][k];
				if(work[next] != UNDEFINED || visited[next] == true){

					continue;
				}

				if(work[Q.front()] == A){

					work[next] = B;

				}else if(work[Q.front()] == B){

					work[next] = A;

				}else{

					return false;
				}
				visited[next] = true;
				Q.push(next);
				TMP.push_back(next);
			}
			Q.pop();
		}

		for(int k = 0; k < TMP.size(); k++){

			if(work[TMP[k]] == UNDEFINED){

				return false; //塗り残しあり
			}

			int from = TMP[k];

			for(int p = 0; p < G[from].size(); p++){
				if(work[from] == work[G[from][p]]){
					return false; //同色隣接
				}
			}
		}
	}

	return true;
}

void recursive(int group_id,int index){

	if(clear_FLG)return;

	if(index == num_member[group_id]){

		for(int i = 0; i < member[group_id].size(); i++){

			work[member[group_id][i]] = color[member[group_id][i]];
		}

		for(int i = 0; i < member[group_id].size(); i++){

			if(color[member[group_id][i]] == UNDEFINED){

				continue;
			}

			int from = member[group_id][i];

			for(int k = 0; k < G[from].size(); k++){
				if(color[from] == color[G[from][k]]){
					return; //C隣接
				}
			}
		}

		//残りの領域を2色で塗れるか判定
		if(is_OK(group_id)){

			clear_FLG = true;
		}
		return;
	}

	int node_id = member[group_id][index];

	if(color[node_id] == C || count_table[node_id] != 0){

		recursive(group_id,index+1);
		return;
	}

	color[node_id] = C;

	for(int i = 0; i < G[node_id].size(); i++){

		count_table[G[node_id][i]]++;
	}
	recursive(group_id,index+1);

	if(clear_FLG)return;

	color[node_id] = UNDEFINED;

	for(int i = 0; i < G[node_id].size(); i++){

		count_table[G[node_id][i]]--;
	}

	for(int i = 0; i < G[node_id].size(); i++){

		int next = G[node_id][i];

		if(count_table[next] == 0 && color[next] != C){

			color[next] = C;
			for(int k = 0; k < G[next].size(); k++){

				count_table[G[next][k]]++;
			}

			recursive(group_id,index+1);

			color[next] = UNDEFINED;
			for(int k = 0; k < G[next].size(); k++){

				count_table[G[next][k]]--;
			}
		}
	}
}

bool can_three(int group_id){

	vector<Info> info;
	for(int i = 0; i < member[group_id].size(); i++){

		info.push_back(Info(member[group_id][i],G[member[group_id][i]].size()));
	}

	sort(info.begin(),info.end());

	for(int i = 0; i < info.size(); i++){

		member[group_id][i] = info[i].node_id;
	}

	for(int i = 0; i < member[group_id].size(); i++){

		color[member[group_id][i]] = UNDEFINED;
		count_table[member[group_id][i]] = 0;

	}

	clear_FLG = false;
	recursive(group_id,0);

	if(clear_FLG){

		return true;
	}else{

		return false;
	}
}

bool can_two(int group_id){

	color[member[group_id][0]] = A;
	for(int i = 1; i < member[group_id].size(); i++){

		color[member[group_id][i]] = UNDEFINED;
	}

	queue<int> Q;
	Q.push(member[group_id][0]);

	while(!Q.empty()){

		for(int i = 0; i < G[Q.front()].size(); i++){

			int next = G[Q.front()][i];

			if(color[next] != UNDEFINED){

				continue;
			}

			if(color[Q.front()] == A){

				color[next] = B;

			}else{

				color[next] = A;
			}
			Q.push(next);
		}

		Q.pop();
	}

	for(int i = 0; i < member[group_id].size(); i++){

		if(color[member[group_id][i]] == UNDEFINED){

			return false;
		}
	}

	for(int i = 0; i < member[group_id].size(); i++){

		int from = member[group_id][i];

		for(int k = 0; k < G[from].size(); k++){
			if(color[from] == color[G[from][k]]){

				return false;
			}
		}
	}

	return true;
}

Point calc_minus(Point a,Point b){
    Point ret;

    ret.x = a.x-b.x;
    ret.y = a.y-b.y;

    return ret;
}

double norm(Vector a){
	return a.x*a.x+a.y*a.y;
}

double abs(Vector a){
	return sqrt(norm(a));
}

double cross(Vector a,Vector b){
    return a.x*b.y-a.y*b.x;
}

double dot(Vector a,Vector b){
    return a.x*b.x + a.y*b.y;
}

double calc_len(Vector a){
    return sqrt(a.x*a.x+a.y*a.y);
}

//線分ではなく直線と点の距離
double getDistanceLP(Line l,Point p){
    return fabs(cross(calc_minus(l.p[1],l.p[0]),calc_minus(p,l.p[0]))/calc_len(calc_minus(l.p[1],l.p[0])));
}

//点と線分の距離
double getDistanceSP(Line l,Point p){
	if(dot(calc_minus(l.p[1],l.p[0]),calc_minus(p,l.p[0])) < 0.0)return calc_len(calc_minus(p,l.p[0]));
	if(dot(calc_minus(l.p[0],l.p[1]),calc_minus(p,l.p[1])) < 0.0)return calc_len(calc_minus(p,l.p[1]));
	return getDistanceLP(l,p);
}

double calc_slope(Line A){

	if(A.p[0].x > A.p[1].x){

		swap(A.p[0],A.p[1]);
	}

	if(fabs(A.p[0].x-A.p[1].x) < EPS){

		return DBL_MAX;

	}else if(fabs(A.p[0].y-A.p[1].y) < EPS){

		return 0;

	}else{

		return (A.p[0].y-A.p[1].y)/(A.p[0].x-A.p[1].x);
	}
}

//2つの線分に、EPS以上の重なりがあるか調べる関数
bool is_Overwrap(Line A,Line B){

	if(fabs(calc_slope(A)-calc_slope(B)) >= EPS){

		return false;
	}

	if(getDistanceSP(B,A.p[0]) < EPS || getDistanceSP(B,A.p[1]) < EPS){ //線分Aのどちらかの端点がB上にある場合

		if(getDistanceSP(B,A.p[0]) < EPS && getDistanceSP(B,A.p[1]) < EPS){ //両端がB上

			return true;

		}else{

			Point cross_point;

			if(getDistanceSP(B,A.p[0]) < EPS){

				cross_point = A.p[0];

			}else{

				cross_point = A.p[1];
			}

			if(cross_point == B.p[0] || cross_point == B.p[1]){

				//Do nothing

			}else{

				return true;
			}
		}
	}

	if(getDistanceSP(A,B.p[0]) < EPS || getDistanceSP(A,B.p[1]) < EPS){

		if(getDistanceSP(A,B.p[0]) < EPS && getDistanceSP(A,B.p[1]) < EPS){ //両端がA上

			return true;

		}else{

			Point cross_point;

			if(getDistanceSP(A,B.p[0]) < EPS){

				cross_point = B.p[0];

			}else{

				cross_point = B.p[1];
			}

			if(cross_point == A.p[0] || cross_point == A.p[1]){

				//Do nothing

			}else{

				return true;
			}
		}

	}

	return false;
}


void func(){

	init();

	int num_points;
	double X,Y;

	for(int i = 0; i < N; i++){

		scanf("%d",&num_points);

		for(int loop = 0; loop < num_points; loop++){

			scanf("%lf %lf",&X,&Y);

			POLYGON[i].push_back(Point(X,Y));
		}
	}

	//グラフ作成
	for(int i = 0; i < N-1; i++){
		for(int k = i+1; k < N; k++){
			bool FLG = false;
			for(int a = 0; a < POLYGON[i].size(); a++){

				Line tmp_line = Line(POLYGON[i][a],POLYGON[i][(a+1)%POLYGON[i].size()]);

				for(int b = 0; b < POLYGON[k].size(); b++){

					Line adj_line = Line(POLYGON[k][b],POLYGON[k][(b+1)%POLYGON[k].size()]);

					if(is_Overwrap(tmp_line,adj_line)){

						G[i].push_back(k);
						G[k].push_back(i);

						unite(i,k);

						FLG = true;
						break;
					}

				}
				if(FLG)break;
			}
		}
	}

	//国を連結成分ごとに分ける
	for(int i = 0; i < N; i++){

		member[get_boss(i)].push_back(i);
	}

	int maximum = -1;

	for(int i = 0; i < N; i++){

		if(i != get_boss(i))continue;

		if(num_member[i] == 1){

			maximum = max(maximum,1);

		}else if(can_two(i)){

			maximum = max(maximum,2);

		}else if(can_three(i)){

			maximum = max(maximum,3);

		}else{

			maximum = 4;
			break;
		}
	}

	printf("%d\n",maximum);
}

int main(){

	while(true){

		scanf("%d",&N);
		if(N == 0)break;

		func();
	}

	return 0;
}



Measured memory_bytes: 3276