#include <iostream>
#include <cstdio>
#include <vector>
#include <complex>
#include <cmath>
#include <algorithm>
using namespace std;

// * XY座標
#define X real()
#define Y imag()

// * 点の表現
typedef complex<double> P;

// * 許容する誤差ε
const double EPS = 1e-10;
// * Infinity
const double INF = 1e12;
// * 円周率
const double PI = acos(-1.0);
// * 
const P INF_P(INF,INF);

// * complex<double> の順序を定義する
namespace std {
	bool operator < (const P& a, const P& b) {
		return (real(a) != real(b))? real(a) < real(b) : imag(a) < imag(b);
	}
}

// * 点座標のデバッグ出力
void print(P p) {
	cout << "point" << p << ";" << endl;
}

// * 2乗する
double sq(double x) {
	return x * x;
}

// * 2点間の距離
double dist(P p1, P p2) {
	return abs(p1 - p2);
}

// * 2つのスカラーが等しいかどうか
bool equal(double a, double b) {
	return ( fabs( a - b ) < EPS );
}
// * 2つのベクトルが等しいかどうか
bool eq_v(P a, P b) {
	return ( equal( a.X, b.X ) && equal( a.Y, b.Y ) );
}

// ベクトルaの単位ベクトルを求める
P unit(P a) {
	return a / abs(a);
}

// ベクトルaの法線ベクトルを求める
vector<P> normal(P a) {
	vector<P> vp;
	vp.push_back( a * P(0,  1) );
	vp.push_back( a * P(0, -1) );
	return vp;
}

// ベクトル a の単位法線ベクトルを求める
vector<P> normal_unit(P a) {
	vector<P> vp;
	vp.push_back( (a * P(0,  1)) / abs(a) );
	vp.push_back( (a * P(0, -1)) / abs(a) ) ;
	return vp;
}

// * 内積 (dot product) : a・b = |a||b|cosΘ
double dot(P a, P b) {
	return real( conj(a) * b );
}

// * 外積 (cross product) : a×b = |a||b|sinΘ
double cross(P a, P b) {
	return imag( conj(a) * b ) ;
}

// CCW : 反時計回り (Counter Clock Wise)
// CW : 時計回り (Clock Wise)
enum {CCW=1, CW=-1, ON=0};
// * 3点がどちら回りであるか返す, 3点が1直線に乗っているときは ON を返す
int ccw(const P &a, P b, P c) {
	b-=a, c-=a;
	double rotdir = cross(b,c);
	if( rotdir >  EPS ) return CCW;
	if( rotdir < -EPS ) return CW;
	return ON;
}

// ラジアンから度数へ変換して返す
double to_deg(double rad) {
	return rad * 180.0 / PI;
}

// 度数からラジアンに変換して返す
double to_rad(double deg) {
	return deg * PI / 180.0;
}

// 原点を軸に点 p を角度 a (ラジアン)だけ回転させた点を返す
P rot(P p, double a){
	double x = p.X * cos(a) - p.Y * sin(a);
	double y = p.X * sin(a) + p.Y * cos(a);
	return P(x,y);
}

// 点 a を軸に点 b を角度 a (ラジアン)だけ回転させた点を返す
P rot2(P a, P b, double angle){
	P p = b - a;
	return rot( p , angle ) + a;
}

// 線分の表現
struct Segment{
	// 点 a, b を端点とする線分
	P a, b;
	// コンストラクタで初期化
	Segment(P a_, P b_) {
		if( b_ < a_ ) swap(a_,b_);
		a = a_; b = b_;
	}
	// 線分の長さを返す
	double length() { return abs(a-b); }
	// 中点を返す
	P mid() { return P( (a.X+b.X)/2.0 , (a.Y+b.Y)/2.0 ); }
	// 点 p が線分上にあるかどうか
	bool contain(P p) { return (abs(a-p) + abs(p-b) < abs(a-b) + EPS); }
	// 線分と点 p の距離
	double distance(P p) {
		if( dot(b-a, p-a) < EPS ) return abs(p-a);
		if( dot(a-b, p-b) < EPS ) return abs(p-b);
		return abs(cross(b-a, p-a)) / abs(b-a);
	}
	// 線分の交差判定
	bool is_intersection(const Segment& s) {
		return ( cross(b-a, s.a-a) * cross(b-a, s.b-a) < EPS ) &&
			   ( cross(s.b-s.a, a-s.a) * cross(s.b-s.a, b-s.a) < EPS );
	}
	// 線分の交差判定と交点計算
	bool intersection(Segment s, P& p) {
		bool result = is_intersection( s );
		if( result ) {
			double d1 = abs( cross(s.b-s.a, a-s.a) );
			double d2 = abs( cross(s.b-s.a, b-s.a) );
			
			if( abs(d1) < EPS && abs(d2) < EPS ) { // 2つの線分が同じ直線上にあるとき
				if( this->contain(s.a) ) {
					p = s.a;
				}else if( this->contain(s.b) ) {
					p = s.b;
				}else if( s.contain( a ) ) {
					p = a;
				}else if( s.contain( b ) ) {
					p = b;
				}else {
					p = INF_P;
				}
			}else if( abs(d1) < EPS ) {
				p = INF_P;
			}else {
				double t = d1 / (d1 + d2);
				p = a + (b-a) * t;
			}
		}else{
			p = INF_P;
		}
		return result;
	}
	// 線分と線分の距離
	double distance(Segment s) {
		if( this->is_intersection( s ) ) { // 交差するとき	
			double d1 = abs( cross(s.b-s.a, a-s.a) );
			double d2 = abs( cross(s.b-s.a, b-s.a) );
			
			if( abs(d1) < EPS && abs(d2) < EPS ) { // 2つの線分が同じ直線上にあるとき
				if( this->contain(s.a) || this->contain(s.b) ) {
					return 0.0;
				}else if( s.contain( a ) || s.contain( b ) ) {
					return 0.0;
				}
			}else{
				return 0.0;
			}
		}
		double A = this->distance(s.a);
		double B = this->distance(s.b);
		double C = s.distance( (*this).a );
		double D = s.distance( (*this).b );
		return min( min(A,B) , min(C,D) );
	}
	// 点 p を軸に 線分 を 角度 angle だけ回転移動 (角度はラジアン)
	Segment rot(P p, double angle) {
		P a_ = rot2( p , a , angle );
		P b_ = rot2( p , b , angle );
		return Segment( a_ , b_ );
	}
	// デバッグ出力
	void print() { printf("line(%f,%f,%f,%f); \n", a.X, a.Y, b.X, b.Y ); }
};
// Segment の順序を定義 (x座標の小さい端点で比較)
bool operator < (const Segment& s1, const Segment& s2) { return s1.a < s2.a; }

// 直線の表現 ((*this)[0]) と ((*this)[1]) を通る直線
struct Line : public vector<P> {
	Line(const P &a, const P &b) {
		push_back(a); push_back(b);
	}
	Line(vector<P> v){
		push_back(v[0]); push_back(v[1]);
	}
	Line(){}
	// 2直線の直交判定
	bool orthogonal(const Line& l) {
		return equal( dot( (*this)[0] - (*this)[1] , l[0] - l[1] ) , 0.0 );
	}
	// 2直線の平行判定
	bool parallel(const Line& l) {
		return equal( cross( (*this)[0] - (*this)[1] , l[0] - l[1] ), 0.0 );
	}
	// 点 p が直線上に乗っているか
	bool contain(P p) {
		return equal( cross( (*this)[1] - (*this)[0] , p - (*this)[0] ) , 0.0 );
	}
	// 直線と点 p の距離
	double distance(P a) {
		P p1 = (*this)[0];
		P p2 = (*this)[1];
		return abs( cross( p2 - p1 , a - p1) ) / abs(p2 - p1);
	}
	// 直線の交差判定
	bool is_intersection(const Line& l) {
		return !equal( cross( (*this)[0] - (*this)[1], l[0] - l[1] ) , 0.0 );
	}
	// 直線の交差判定と交点計算
	bool intersection(Line l, P& p) {
		bool result = this->is_intersection( l );
		if( result ) {
			P a = (*this)[1] - (*this)[0];
			P b = l[1] - l[0];
			p = (*this)[0] + a * cross(b, l[0] - (*this)[0]) / cross(b, a);
		}else {
			p = INF_P;
		}
		return result;
	}
	// 角度を返す [-π,π]
	double get_dir() {
		P p = (*this)[0] - (*this)[1];
		return atan2( p.Y , p.X );
	}
	// 双対変換
	P dual(const Line &l) {
		const P p = l[0], q = l[1];
		return P( imag(p - q) / real(p - q), cross(p, q) / real(q - p) );
	}
	// 点を線対称に移動させる
	P symmetry(P a) {
		double x,y;
		double x1 = (*this)[0].X;
		double x2 = (*this)[1].X;
		double y1 = (*this)[0].Y;
		double y2 = (*this)[1].Y;
		double xq = a.X;
		double yq = a.Y;
		if( x2 == x1 ) {
			x = -xq;
			y = yq;
		}else{
			double m = (y2 - y1) / (x2 - x1);
			double c = y2 - m * x2;
			x = (2 * m * yq - xq * (m * m - 1) - 2 * m * c) / (m * m + 1);
		    y = (2 * m * xq + yq * (m * m - 1) + 2 * c) / (m * m + 1);
		}
		return P(x,y);
	}
	// 点 p を軸に 直線 を 角度 angle だけ回転移動 (角度はラジアン)
	Line rot(P p, double angle) {
		P a = rot2( p , (*this)[0] , angle );
		P b = rot2( p , (*this)[1] , angle );
		return Line( a , b );
	}
	// デバッグ出力
	void print() {
		P p1 = (*this)[0];
		P p2 = (*this)[1];
		double dx = p1.X - p2.X;
		double dy = p1.Y - p2.Y;
		::print( p1 );
		::print( p2 );
		p1.X += 100 * dx;
		p1.Y += 100 * dy;
		p2.X -= 100 * dx;
		p2.Y -= 100 * dy;
		Segment s( p1 , p2 );
		s.print();
	}
};

// 円の表現
struct Circle{
	// 円の中心
	P p;
	// 円の半径
	double r;
	// コンストラクタで初期化
	Circle(P p_, double r_) { p = p_; r = r_; }
	// 円の面積
	double get_area() { return r * r * PI; }
	// 円周
	double circumference() { return 2.0 * r * PI; }
	// 点 a が円の内部にあるかどうか
	bool is_inside(P a) { return ( dist(a,p) <= r+EPS ); }
	// 点 a が円周上にあるかどうか
	bool on_boundary(P a) { return equal( dist(a,p) , r ); }
	// 角度 [a,b] の弧の長さを返す (a,bはラジアン)
	double arc(double a, double b) {
		while( a > b ) b += 2.0 * PI;
		return r * (b - a);
	}
	// 角度 [a,b] の扇形の面積を返す (a,bはラジアン)
	double sector(double a, double b) {
		while( a > b ) b += 2.0 * PI;
		return 0.5 * r * r * (b - a);
	}
	// 直線と円の距離 (円の中心の点と直線の距離 - 円の半径)
	double distance(Line l){
		return l.distance( p ) - r;
	}
	// 点 a を通る接線を返す.
	pair<Line,Line> tangent(P a) {
		pair<Line,Line> pl;
		if( this->on_boundary( a ) ) { // 点 a が円周上にあるとき
			Line l( normal(a - p) ); // 法線ベクトル
			l[0] += a;
			l[1] += a;
			pl.first = pl.second = l ;
			return pl;
		}else if( !this->is_inside(a) ) { // 点 a が円の外側にあるとき
			double xp = a.X - p.X;
			double yp = a.Y - p.Y;
			double A = sqrt( sq(xp) + sq(yp) - sq(r) );
			double B = sq(xp) + sq(yp);
			P p1( r * (xp * r + yp * A) / B , r * (yp * r - xp * A) / B );
			P p2( r * (xp * r - yp * A) / B , r * (yp * r + xp * A) / B );
			pl.first = Line( a , p1+p );
			pl.second = Line( a , p2+p );
		}else{ // 点 a が円の内側にあるとき
			pl.first = pl.second = Line(INF_P,INF_P);
		}
		return pl;
	}
	// 等しいかどうか
	bool operator==(const Circle& c) { return (p == c.p && r == c.r) ; };
	// 2つの円の位置関係
	// 2 つの円が離れている => 0
	// 2 つの円が外接する   => 1
	// 2 つの円が交わる     => 2
	// 2 つの円が内接する    => 3 (2つの円が等しいときは内接)
	// 円が含まれている       => 4
	int is_pos(const Circle& c) {
		double d = abs(p - c.p);
		if( r + c.r + EPS < d ) { // 離れている
			return 0;
		}else if( equal( r + c.r , d ) ) { // 外接する
			return 1;
		}else if( equal( fabs(r-c.r) , d ) ) { // 内接する
			return 3;
		}else if( d < fabs(r-c.r) ) { // 含まれる
			return 4;
		}else { // 交わる	
			return 2;
		}
	}
	// 円と直線の交わる点を返す
	pair<P,P> intersection(Line l) {
		pair<P,P> pp;
		if( this->distance(l) > EPS ) { // 円と直線の交点がないとき	
			pp.first = pp.second = INF_P;
		}else { // 円と直線の交点があるとき
			double d = l.distance( p );
			double angle = -l.get_dir();
			// 点 l[0] を軸に点 l[1] と円の中心を回転
			P lp = rot2( l[0] , l[1] , angle );
			P cp = rot2( l[0] , p    , angle );
			double x1 = cp.X - sqrt( sq(r) - sq(d) );
			double x2 = cp.X + sqrt( sq(r) - sq(d) );
			P p1(x1,l[0].Y);
			P p2(x2,l[0].Y);
			// 回転して元に戻す
			p1 = rot2( l[0] , p1 , -angle );
			p2 = rot2( l[0] , p2 , -angle );
			pp.first = p1;
			pp.second = p2;
		}
		return pp;
	}
	// 2 つの円の交わる点を返す
	// 2 つの円が離れている・内包する => 交点は 0 個, (INF_P,INF_P) を返す.
	// 2 つの円が外接 or 内接する   => 交点は 1 個, (firstとsecondに同じ値が入る)
	// 2 つの円が交わる            => 交点は 2 個.
	// 2 つの円は等しい            => 交点は 無限 個, (INF_P,INF_P) を返す.
	pair<P,P> intersection(const Circle& c) {
		if( this->is_pos( c ) == 0 || this->is_pos( c ) == 4 || (*this) == c ) {
			return pair<P,P>(INF_P,INF_P);
		}
		double d = abs( p - c.p );
		double rc = ( d*d + r*r - c.r*c.r ) / (2.0*d);
		double rs = sqrt( r*r - rc*rc );
		P diff = (c.p - p) / d;
		return pair<P,P> ( p + diff * P(rc, rs) , p + diff * P(rc, -rs) );
	}
	// 2つの円の共通接線の計算に使用
	// (flag == true のとき内側の接線, falseのとき外側の接線)
	pair<P,P> common_tangent1(const Circle& c, bool flag) {
		double xp = c.p.X - p.X;
		double yp = c.p.Y - p.Y;
		double R = (flag)? r + c.r : r - c.r ;
		double A = sqrt( sq(xp) + sq(yp) - sq(R) );
		double B = sq(xp) + sq(yp);
		P p1( r * (xp * R + yp * A) / B , r * (yp * R - xp * A) / B );
		P p2( r * (xp * R - yp * A) / B , r * (yp * R + xp * A) / B );
		p1 += p;
		p2 += p;
		return pair<P,P> (p1,p2);
	}
	// 2つの円の共通接線を返す
	// 2 つの円が離れている => 4本
	// 2 つの円が外接する   => 3本 (共通内接線が1つ)
	// 2 つの円が交わる     => 2本　(共通内接線がない)
	// 2 つの円が内接する    => 1本 (共通外接線が1つ)
	// 円が含まれている       => 0本
	vector<Line> common_tangent(Circle c) {
		vector<Line> vl;
		int pos = this->is_pos( c );
		// 2つの共通内接線を求める
		pair<P,P> pp1 = this->common_tangent1( c , true );
		pair<P,P> pp2 = c.common_tangent1( (*this) , true );
		if( pos == 0 ) { // 2つの円が離れているとき
			vl.push_back( Line( pp1.first , pp2.first ) );
			vl.push_back( Line( pp1.second , pp2.second ) );
		}else if( pos == 1 ) { // 外接するとき
			vl.push_back( this->tangent( pp1.first ).first );
		}
		// 2つの共通外接線を求める
		pp1 = this->common_tangent1( c , false );
		pp2 = c.common_tangent1( (*this) , false );
		if( pos <= 2 ) { // 2つの円が交わるとき
			vl.push_back( Line( pp1.first , pp2.second ) );
			vl.push_back( Line( pp1.second , pp2.first ) );
		}else if( pos == 3 ) { // 2つの円が内接するとき
			vl.push_back( this->tangent( pp1.first ).first );
		}
		return vl;
	}
	// デバッグ出力
	void print() { printf("circle(%f,%f,%f); \n", p.X, p.Y, r ); }
};


int main(){
	int n;
	const double r = 1.0;
	while( scanf("%d", &n ) ){
		if( n == 0 ) break;
		
		vector<Circle> v;
		vector<P> vp;
		
		for(int i=0 ; i < n ; i++ ){
			double x, y;
			scanf("%lf,%lf", &x, &y );
			v.push_back( Circle( P(x,y) , r ) );
		}
		
		// 円と円の交点を求める
		for(int i=0 ; i < v.size() ; i++ ){
			for(int j = i+1 ; j < v.size() ; j++ ){
				pair<P,P> pp = v[i].intersection( v[j] );
				// 交点が 0個 or 無限個のときは continue
				if( pp.first == INF_P ) continue;
				
				vp.push_back( pp.first );
				vp.push_back( pp.second );
			}
		}
		
		// 各交点について円が何枚重なっているか調べる
		int ans = 1;
		for(int i=0 ; i < vp.size() ; i++ ){
			P p = vp[i];
			int cnt = 0;
			for(int j=0 ; j < v.size() ; j++ ){
				if( v[j].is_inside(p) ){
					cnt++;
				}
			}
			ans = max( ans , cnt );
		}
		// 出力
		cout << ans << endl;
	}
}

Measured memory_bytes: 0