use std::{io::{stdin, Read}, collections::HashSet};

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
struct Pt(i32, i32);

fn main() {
    let mut stdin = stdin();
    let mut buf = String::new();
    stdin.read_to_string(&mut buf).unwrap();

    let mut tiles: HashSet<Pt> = HashSet::new();
    let mut lowest: i32 = 0;
    
    for line in buf.split("\n") {
        let pts: Vec<Pt> = line.split(" -> ").map(|l| {
            let pts: Vec<&str> = l.split(",").collect();
            Pt(pts[0].parse().unwrap(), pts[1].parse().unwrap())
        }).collect();
        let mut pos = pts[0];
        tiles.insert(pos);
        if pos.1 > lowest { lowest = pos.1; }
        for i in 1..pts.len() {
            let diff = if pts[i].1 == pos.1 {
                if pts[i].0 < pos.0 { Pt(-1, 0) } else { Pt(1, 0) }
            } else {
                if pts[i].1 < pos.1 { Pt(0, -1) } else { Pt(0, 1) }
            };
            while pos != pts[i] {
                pos.0 += diff.0;
                pos.1 += diff.1;
                tiles.insert(pos);
                if pos.1 > lowest { lowest = pos.1; }
            }
        }
    }
    let sand_point = Pt(500, 0);
    let mut sands: u32 = 0;
    'a: loop {
        let mut p = sand_point;
        loop {
            if is_free(&tiles, lowest, Pt(p.0, p.1 + 1)) {
                p.1 += 1;
            } else if is_free(&tiles, lowest, Pt(p.0 - 1, p.1 + 1)) {
                p.0 -= 1;
                p.1 += 1;
            } else if is_free(&tiles, lowest, Pt(p.0 + 1, p.1 + 1)) {
                p.0 += 1;
                p.1 += 1;
            } else {
                tiles.insert(p);
                sands += 1;
                if p == sand_point { break 'a; } // part 2
                break;
            }
            // if p.1 > lowest { break 'a; } // part 1
        }
    }
    println!("{}", sands);
}

fn is_free(tiles: &HashSet<Pt>, lowest: i32, pt: Pt) -> bool {
    // !tiles.contains(&pt) // part 1
    if pt.1 > lowest + 1 { false } // part 2
    else { !tiles.contains(&pt) }
}