#include <bits/stdc++.h>
using namespace std;
template <class T>
struct point {
  T x, y;
  point &operator+=(const point &a) {
    x += a.x;
    y += a.y;
  }
  point &operator-=(const point &a) {
    x -= a.x;
    y -= a.y;
  }
  point operator+(const point &a) const { return (point){x + a.x, y + a.y}; }
  point operator-(const point &a) const { return (point){x - a.x, y - a.y}; }
  operator point<double>() const { return (point<double>){x, y}; }
};
template <class T>
point<T> operator*(T c, const point<T> &a) {
  return (point<T>){c * a.x, c * a.y};
}
point<double> &operator/=(point<double> &a, double c) {
  a.x /= c;
  a.y /= c;
  return a;
}
template <class T>
point<double> operator/(const point<T> &a, double c) {
  return (point<double>){a.x / c, a.y / c};
}
template <class T>
bool operator<(const point<T> &a, const point<T> &b) {
  return (a.x < b.x || ((a.x == b.x) && (a.y < b.y)));
}
template <class T>
bool operator==(const point<T> &a, const point<T> &b) {
  return a.x == b.x && a.y == b.y;
}
template <class T>
bool operator!=(const point<T> &a, const point<T> &b) {
  return a.x != b.x || a.y != b.y;
}
bool operator<(const point<double> &a, const point<double> &b) {
  return (a.x + (1e-10) < b.x ||
          (abs(a.x - b.x) < (1e-10) && (a.y + (1e-10) < b.y)));
}
bool operator==(const point<double> &a, const point<double> &b) {
  return abs(a.x - b.x) < (1e-10) && abs(a.y - b.y) < (1e-10);
}
bool operator!=(const point<double> &a, const point<double> &b) {
  return abs(a.x - b.x) > (1e-10) || abs(a.y - b.y) > (1e-10);
}
template <class T>
T dot(const point<T> &a, const point<T> &b) {
  return a.x * b.x + a.y * b.y;
}
template <class T>
T cross(const point<T> &a, const point<T> &b) {
  return a.x * b.y - a.y * b.x;
}
template <class T>
double abs(const point<T> &a) {
  return sqrt(a.x * a.x + a.y * a.y);
}
template <class T>
T abs2(const point<T> &a) {
  return a.x * a.x + a.y * a.y;
}
point<double> rot(const point<double> &a, double theta) {
  return (point<double>){a.x * cos(theta) - a.y * sin(theta),
                         a.x * sin(theta) + a.y * cos(theta)};
}
template <class T>
double arg(const point<T> &a) {
  double t = atan2(a.y, a.x);
  return t < 0. ? t + 2. * (3.141592653589793238462643383279) : t;
}
template <class T>
struct line {
  point<T> a, b;
  operator line<double>() const { return (line<double>){a, b}; }
};
template <class T>
struct segment {
  point<T> a, b;
  operator line<T>() const { return (line<T>){a, b}; }
};
template <class T>
struct polygon : vector<point<T> > {
  polygon() {}
  polygon(int n) : vector<point<T> >(n) {}
};
template <class T>
struct circle {
  point<T> c;
  T r;
};
template <class T>
point<double> proj(const point<T> &p, const line<T> &L) {
  return L.a + dot(p - L.a, L.b - L.a) / abs2(L.b - L.a) * (L.b - L.a);
}
enum { CCW = 1, CW = -1, ON = 0 };
int ccw(const point<double> &a, const point<double> &b,
        const point<double> &c) {
  double rdir = cross(b - a, c - a);
  if (rdir > 0) return CCW;
  if (rdir < 0) return CW;
  return ON;
}
template <class T>
double dist(const point<T> &a, const point<T> &b) {
  return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
template <class T>
T dist2(const point<T> &a, const point<T> &b) {
  return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
template <class T>
double dist(const line<T> &L, const point<T> &p) {
  return abs(cross(L.b - L.a, p - L.a)) / dist(L.a, L.b);
}
template <class T>
double dist(const segment<T> &S, const point<T> &p) {
  if (dot(S.b - S.a, p - S.a) <= 0) return dist(p, S.a);
  if (dot(S.a - S.b, p - S.b) <= 0) return dist(p, S.b);
  return abs(cross(S.b - S.a, p - S.a)) / dist(S.a, S.b);
}
template <class T>
double dist2(const segment<T> &S, const point<T> &p) {
  if (dot(S.b - S.a, p - S.a) <= 0) return dist2(p, S.a);
  if (dot(S.a - S.b, p - S.b) <= 0) return dist2(p, S.b);
  return (double)cross(S.b - S.a, p - S.a) * cross(S.b - S.a, p - S.a) /
         dist(S.a, S.b);
}
int intersect(const circle<double> &C, const line<double> &L) {
  point<double> m = proj(C.c, L);
  double d = abs(C.c - m);
  if (C.r + (1e-10) < d) return 0;
  if (C.r - (1e-10) < d) return 1;
  return 2;
}
template <class T>
bool intersect(const circle<T> &C, const segment<T> &S) {
  return dist2(S, C.c) <= C.r * C.r;
}
bool intersect(const circle<double> &C, const segment<double> &S) {
  return dist(S, C.c) < C.r + (1e-10);
}
template <class T>
bool intersect(const segment<T> &S1, const segment<T> &S2) {
  if (max(S1.a.x, S1.b.x) < min(S2.a.x, S2.b.x) ||
      max(S1.a.y, S1.b.y) < min(S2.a.y, S2.b.y) ||
      max(S2.a.x, S2.b.x) < min(S1.a.x, S1.b.x) ||
      max(S2.a.y, S2.b.y) < min(S1.a.y, S1.b.y))
    return false;
  return ccw(S1.a, S1.b, S2.a) * ccw(S1.a, S1.b, S2.b) <= 0 &&
         ccw(S2.a, S2.b, S1.a) * ccw(S2.a, S2.b, S1.b) <= 0;
}
template <class T>
bool cover(const point<T> &a, const point<T> &b, const point<T> &c,
           const point<T> &p) {
  int d1 = ccw(p, a, b);
  int d2 = ccw(p, b, c);
  int d3 = ccw(p, c, a);
  return !((d1 == CCW && d2 == CW) || (d1 == CCW && d3 == CW) ||
           (d2 == CCW && d3 == CW) || (d1 == CW && d2 == CCW) ||
           (d1 == CW && d3 == CCW) || (d2 == CW && d3 == CCW));
}
template <class T>
bool cover(const circle<T> &C, const point<T> &p) {
  return dist2(C.c, p) <= C.r * C.r;
}
bool cover(const circle<double> &C, const point<double> &p) {
  return dist(C.c, p) < C.r + (1e-10);
}
double inradius(double a, double b, double c) {
  return sqrt((b + c - a) * (c + a - b) * (a + b - c) / (a + b + c)) / 2.;
}
circle<double> circumcircle(const point<double> &a, point<double> b,
                            point<double> c) {
  b -= a;
  c -= a;
  double A2 = abs2(b - c), B2 = abs2(b), C2 = abs2(c);
  double d = 2 * cross(b, c);
  double nx = c.y * B2 - b.y * C2;
  double ny = b.x * C2 - c.x * B2;
  double r2 = A2 * B2 * C2 / (d * d);
  return (circle<double>){a + (point<double>){nx / d, ny / d}, sqrt(r2)};
}
double circumradius(double a, double b, double c) {
  return a * b * c /
         sqrt((a + b + c) * (b + c - a) * (c + a - b) * (a + b - c));
}
template <class T>
void to_ccw(polygon<T> &G) {
  int n = G.size();
  T A = 0;
  for (int i = 0; i < (n); i++) A += cross(G[i], G[(i + 1) % n]);
  if (A < 0) reverse(G.begin(), G.end());
}
void to_ccw(polygon<double> &G) {
  int n = G.size();
  double A = 0.;
  for (int i = 0; i < (n); i++) A += cross(G[i], G[(i + 1) % n]);
  if (A < -(1e-10)) reverse(G.begin(), G.end());
}
template <class T>
bool is_convex(const polygon<T> &G) {
  int n = G.size();
  for (int i = 0; i < (n); i++)
    if (ccw(G[i], G[(i + 1) % n], G[(i + 2) % n]) == CW) return false;
  return true;
}
template <class T>
T area2(const polygon<T> &G) {
  int n = G.size();
  T a = 0;
  for (int i = 0; i < (n); i++) a += cross(G[i], G[(i + 1) % n]);
  return abs(a);
}
template <class T>
polygon<T> convex_hull(vector<point<T> > &P) {
  int n = P.size();
  polygon<T> CH;
  if (n <= 1) {
    CH.insert(CH.end(), P.begin(), P.end());
    return CH;
  }
  sort(P.begin(), P.end());
  for (int cnt = 0; cnt < (2); cnt++) {
    int m = 0;
    vector<point<T> > half(n);
    for (int i = 0; i < (n); i++) {
      half[m++] = P[i];
      while (m >= 3 && ccw(half[m - 3], half[m - 2], half[m - 1]) != CCW) {
        half[m - 2] = half[m - 1];
        m--;
      }
    }
    CH.insert(CH.end(), half.begin(), half.begin() + m - 1);
    reverse(P.begin(), P.end());
  }
  return CH;
}
template <class T>
bool intersect(const polygon<T> &G, const segment<T> &S) {
  int n = G.size();
  if (cover(G, S.a) || cover(G, S.b)) return true;
  for (int i = 0; i < (n); i++)
    if (intersect((segment<T>){G[i], G[(i + 1) % n]}, S)) return true;
  return false;
}
int get_intersect(const circle<double> &C1, const circle<double> &C2,
                  vector<point<double> > &res) {
  double r1 = C1.r, r2 = C2.r;
  point<double> p1 = C1.c, p2 = C2.c;
  double d = abs(p1 - p2);
  if (d < (1e-10) && abs(r1 - r2) < (1e-10)) {
    return -1;
  } else if (r1 + r2 < d - (1e-10) || d + (1e-10) < abs(r1 - r2)) {
    return 0;
  } else {
    double a = (r1 * r1 - r2 * r2 + d * d) / (2. * d);
    double h = sqrt(max(r1 * r1 - a * a, 0.));
    point<double> tmp1 = p1 + a / d * (p2 - p1);
    point<double> tmp2 = h / d * (p2 - p1);
    if (abs(tmp2) < (1e-10)) {
      res.push_back(tmp1);
      return 1;
    } else {
      res.push_back((point<double>){tmp1.x - tmp2.y, tmp1.y + tmp2.x});
      res.push_back((point<double>){tmp1.x + tmp2.y, tmp1.y - tmp2.x});
      return 2;
    }
  }
}
int main() {
  int n;
  while (scanf("%d", &n), n) {
    vector<circle<double> > c(n);
    for (int i = 0; i < (n); i++)
      scanf("%lf,%lf", &c[i].c.x, &c[i].c.y), c[i].r = 1.;
    vector<point<double> > inst;
    inst.clear();
    for (int i = 0; i < (n); i++) {
      for (int j = i + 1; j < n; j++) {
        vector<point<double> > dummy;
        dummy.clear();
        int ans = get_intersect(c[i], c[j], dummy);
        if (ans != -1 && ans != 0) {
          for (int j = 0; j < (dummy.size()); j++) inst.push_back(dummy[j]);
        }
      }
    }
    inst.erase(unique(inst.begin(), inst.end()), inst.end());
    int m = inst.size();
    int res = 0;
    for (int j = 0; j < (m); j++) {
      int curr = 0;
      for (int i = 0; i < (n); i++)
        if (cover(c[i], inst[j])) curr++;
      res = max(res, curr);
    }
    printf("%d\n", res);
  }
  return 0;
}
