using System.Text.RegularExpressions;
using System.Text;
using System.Collections.Immutable;
using System.Drawing;

public class Day12 : IDay
{
    public void PrintMap(char[,] map)
    {
        for (int r = 0; r < map.GetLength(0); r++)
        {
            for (int c = 0; c < map.GetLength(1); c++)
            {
                Console.Write(map[r, c]);
            }
            Console.WriteLine();
        }
    }
    public void Run(string input)
    {
        var map = ParseRows(input);
        var regions = new List<Region>();
        for (int r = 0; r < map.GetLength(0); r++)
        {
            for (int c = 0; c < map.GetLength(1); c++)
            {
                if (!regions.Any(re => re.Contains((r, c))))
                {
                    regions.Add(FindRegion((r, c), map));
                }
            }
        }
        long price = 0;
        foreach (var r in regions)
        {
            //Console.WriteLine(r.ToString());
            price += r.Price();
        }
        Console.WriteLine(price);

    }
    static (int, int) UP = (-1, 0);
    static (int, int) DOWN = (1, 0);
    static (int, int) LEFT = (0, -1);
    static (int, int) RIGHT = (0, 1);

    public static (int, int) GoDir((int, int) p, (int, int) d)
    {

        var (r, c) = (p.Item1 + d.Item1, p.Item2 + d.Item2);
        return (r, c);
    }
    public (char, (int, int))? Try((int, int) p, (int, int) d, char[,] map)
    {
        var (r, c) = GoDir(p, d);
        if (r < 0)
            return null;
        if (c < 0)
            return null;
        if (r >= map.GetLength(0))
            return null;
        if (c >= map.GetLength(1))
            return null;
        return (map[r, c], (r, c));
    }
    public List<(int, int)> FindPoints(char p, (int, int) start, char[,] map, bool[,] visited)
    {
        var (r, c) = start;
        if (visited[r, c])
            return [];
        visited[r, c] = true;

        var path = new List<(int, int)>() { start };
        var u = Try(start, UP, map);
        if (u.HasValue)
        {
            var (nr, nc) = u.Value.Item2;
            if (u.Value.Item1 == p && !visited[nr, nc])
            {
                path.AddRange(FindPoints(p, (nr, nc), map, visited));
            }
        }
        u = Try(start, DOWN, map);
        if (u.HasValue)
        {
            var (nr, nc) = u.Value.Item2;
            if (u.Value.Item1 == p && !visited[nr, nc])
            {
                path.AddRange(FindPoints(p, (nr, nc), map, visited));
            }
        }
        u = Try(start, LEFT, map);
        if (u.HasValue)
        {
            var (nr, nc) = u.Value.Item2;
            if (u.Value.Item1 == p && !visited[nr, nc])
            {
                path.AddRange(FindPoints(p, (nr, nc), map, visited));
            }
        }
        u = Try(start, RIGHT, map);
        if (u.HasValue)
        {
            var (nr, nc) = u.Value.Item2;
            if (u.Value.Item1 == p && !visited[nr, nc])
            {
                path.AddRange(FindPoints(p, (nr, nc), map, visited));
            }
        }
        return path;
    }
    public Region FindRegion((int, int) start, char[,] map)
    {
        var path = new List<(int, int)>();
        var visited = new bool[map.GetLength(0), map.GetLength(1)];
        var (r, c) = start;
        char p = map[r, c];
        return new Region()
        {
            P = p,
            Points = FindPoints(p, start, map, visited)
        };

    }
    public class Region
    {
        public char P { get; set; }
        public List<(int, int)> Points { get; set; }
        public Region()
        {
            Points = new List<(int, int)>();
            perimeter = 0;
            sides = 0;
        }
        public bool Contains((int, int) p)
        {
            return Points.Contains(p);
        }
        public override string ToString()
        {
            return $"{P} ({Perimeter()},{A},{NumSides()}): {string.Join(',', Points)}";
        }
        public int A => Points.Count;
        private long perimeter;
        public long Perimeter()
        {
            if (perimeter > 0)
                return perimeter;
            perimeter = 0;
            foreach (var p in Points)
            {
                int peri = 4;
                if (Points.Contains(GoDir(p, UP)))
                    peri--;
                if (Points.Contains(GoDir(p, DOWN)))
                    peri--;
                if (Points.Contains(GoDir(p, LEFT)))
                    peri--;
                if (Points.Contains(GoDir(p, RIGHT)))
                    peri--;
                perimeter += peri;
            }
            return perimeter;
        }
        public long Price()
        {
            return A * NumSides();
        }
        private long sides;
        public long NumSides()
        {
            if (sides > 0)
                return sides;
            sides = 0;
            foreach (var p in Points)
            {
                var u = Points.Contains(GoDir(p, UP));
                var d = Points.Contains(GoDir(p, DOWN));
                var r = Points.Contains(GoDir(p, RIGHT));
                var l = Points.Contains(GoDir(p, LEFT));

                var ul = Points.Contains(GoDir(GoDir(p, UP), LEFT));
                var ur = Points.Contains(GoDir(GoDir(p, UP), RIGHT));
                var dl = Points.Contains(GoDir(GoDir(p, DOWN), LEFT));
                var dr = Points.Contains(GoDir(GoDir(p, DOWN), RIGHT));

                if (u && l && !ul) // inside upleft corner
                    sides++;
                if (!u && !l) // outside upleft corner
                    sides++;
                if (u && r && !ur) // inside upright corner
                    sides++;
                if (!u && !r) // outside upright corner
                    sides++;
                if (d && l && !dl) //inside downleft corner
                    sides++;
                if (!d && !l) //outside downleft corner
                    sides++;
                if (d && r && !dr) //inside downright corner
                    sides++;
                if (!d && !r) //outside downright corner
                    sides++;


            }
            return sides;
        }
    }
    public static int Angle((int, int) dir)
    {
        if (dir == UP)
            return 90;
        if (dir == LEFT)
            return 180;
        if (dir == DOWN)
            return 270;
        if (dir == RIGHT)
            return 360;
        throw new NotSupportedException();
    }
    public static (int, int) Dir((int, int) a, (int, int) b)
    {
        return (b.Item1 - a.Item1, b.Item2 - a.Item2);
    }
    public static int Dist((int, int) a, (int, int) b)
    {
        return Math.Abs(a.Item1 - b.Item1) + Math.Abs(a.Item2 - b.Item2);
    }
    public char[,] ParseRows(string input)
    {
        List<char[]> rows = new();
        using (StringReader sr = new StringReader(input))
        {
            string? line;
            while ((line = sr.ReadLine()) != null && !string.IsNullOrWhiteSpace(line))
            {
                rows.Add(line.Trim().ToCharArray());
            }

        }
        int width = rows[0].Count();
        int height = rows.Count;
        char[,] map = new char[height, width];
        for (int r = 0; r < rows.Count; r++)
        {
            for (int c = 0; c < rows[0].Count(); c++)
            {
                map[r, c] = rows[r][c];
            }
        }
        return map;


    }
}