テトラミノビンゴ

CodeIQのテトラミノビンゴの解答
解答に1.5H程度かかったらしい。
実行時間は0.35秒程度。

```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace CodeIQ524
{
    class Program
    {
        const int Size = 5;

        static int[] hitNumbers;

        class Island : List<Point>
        {
            private int minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;

            // 範囲を指定する追加のオーバーロード
            public new void Add(Point p)
            {
                base.Add(p);

                minX = Math.Min(minX, p.X);
                minY = Math.Min(minY, p.Y);
                maxX = Math.Max(maxX, p.X);
                maxY = Math.Max(maxY, p.Y);
            }

            // 包括矩形を取得
            public Rectangle BoundingRect
            {
                get
                {
                    return new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1);
                }
            }

            // 範囲の幅と高さ
            public int Width { get { return BoundingRect.Width; } }
            public int Height { get { return BoundingRect.Height; } }

            // テトラミノであるかどうかのフラグ
            public bool IsTetramino { get { return this.Count == 4; } }

            // コーナーの座標
            private List<Point> Corners 
            {
                get {
                    return new List<Point> {
                        new Point(minX, minY),
                        new Point(minX, maxY),
                        new Point(maxX, minY),
                        new Point(maxX, maxY)
                    };
                }
            }

            // 指定したリストがいくつヒットしているかどうかを返す関数
            private int getHitCount(List<Point> points) 
            {
                return points.Where(x => this.Exists(y => y == x)).Count();
            }

            // コーナーを占有している数
            private int CornerCount
            {
                get
                {
                    return getHitCount(Corners);
                }
            }

            // 対角コーナーを占有しているかどうかを示す
            private bool IsDiagonal
            {
                get
                {
                    return getHitCount(new List<Point> { Corners[0], Corners[3] }) == 2 || getHitCount(new List<Point> { Corners[1], Corners[2] }) == 2;
                }
            }

            // テトラミノの様式を返す
            public String Judge
            {
                get
                {
                    // 数が4でなければエラー
                    if (!IsTetramino)
                        return null;

                    // 幅か高さが4ならI
                    if (Width == 4 || Height == 4)
                        return "I";

                    // 幅も高さも2ならO
                    if (Width == 2 && Height == 2)
                        return "O";

                    // 3コーナーを占有していたらL
                    if (CornerCount == 3)
                        return "L";

                    // 対角コーナーを占有していたらS
                    if (IsDiagonal)
                        return "S";

                    // いずれでもなければT
                    return "T";
                }
            }
        }

        class Board
        {
            // 盤面の実態
            bool[,] hitMap = new bool[Size, Size];

            // 数字とのマッピング
            Dictionary<int, Point> mapping = new Dictionary<int, Point>();

            // コンストラクタ
            public Board(int[,] pattern)
            {
                for (int x = 0; x < Size; x++)
                {
                    for (int y = 0; y < Size; y++)
                    {
                        mapping[pattern[x, y]] = new Point(x, y);
                    }
                }
            }

            public void setPattern(int[] pattern) 
            {
                for (int x = 0; x < Size; x++)
                {
                    for (int y = 0; y < Size; y++)
                    {
                        mapping[pattern[x + y * Size]] = new Point(x, y);
                    }
                }
            }

            // 問題様式のコンストラクタ
            public Board(String pattern)
            {
                List<int> numbers = new List<int>();

                foreach (var row in pattern.Split('/')) 
                {
                    numbers.AddRange(row.Split(',').Select(x => int.Parse(x)));
                }

                setPattern(numbers.ToArray());
            }

            // 数字を指定して座標を得る
            public Point? tryHit(int num)
            {
                if (mapping.ContainsKey(num))
                {
                    hitMap[mapping[num].X, mapping[num].Y] = true;
                    return mapping[num];
                }

                return null;
            }

            // 位置が盤面内かどうかの判断
            private bool inRange(Point p)
            {
                return p.X >= 0 && p.Y >= 0 && p.X < Size && p.Y < Size;
            }

            // 位置がヒットしているかどうかの判断
            private bool isHit(Point p)
            {
                return hitMap[p.X, p.Y];
            }

            public Island getIsland(Point p) 
            {
                Queue<Point> task = new Queue<Point>();
                List<Point> visited = new List<Point>();

                Island result = new Island();

                if (!isHit(p))
                    return result;

                task.Enqueue(p);
                visited.Add(p);
                result.Add(p);

                while (task.Count > 0)
                {
                    var chk = task.Dequeue();

                    // 上下左右について、未チェックで範囲内か調べる
                    for (int i = 0; i < 4; i++)
                    {
                        int dx = new int[] { 1, 0, -1, 0 }[i];
                        int dy = new int[] { 0, 1, 0, -1 }[i];

                        Point newPoint = new Point(chk.X + dx, chk.Y + dy);
                        if (inRange(newPoint) && !visited.Exists(x => x == newPoint))
                        {
                            // チェックリストに追加
                            visited.Add(newPoint);

                            // ヒットしていれば、結果に保存
                            if (isHit(newPoint))
                            {
                                result.Add(newPoint);
                                task.Enqueue(newPoint);
                            }
                        }
                    }
                }

                return result;
            }

            public String getResult(IEnumerable<int> numberList)
            {
                foreach (var number in numberList)
                {
                    var p = tryHit(number);
                    if (p != null)
                    {
                        // ヒットしたら、島を取得
                        var island = getIsland(p.Value);
                        var result = island.Judge;
                        if (result != null)
                            return result;
                    }
                }

                return null;
            }
        }

        static void Main(string[] args)
        {
            //Test();
            int[] result = new int[5];
            String resultType = "ILOST";
            DateTime start = DateTime.Now;

            hitNumbers = System.Console.ReadLine().Split(',').Select(x => int.Parse(x)).ToArray();
            String line;
            while ((line = System.Console.ReadLine()) != null)
            {
                Board board = new Board(line);
                String r = board.getResult(hitNumbers);
                if (r != null)
                {
                    result[resultType.IndexOf(r)]++;
                }
            }

            System.Diagnostics.Debug.WriteLine(String.Join(",", Enumerable.Range(0, resultType.Length).Select(x => resultType[x] + ":" + result[x])));
            System.Diagnostics.Debug.WriteLine(DateTime.Now - start);
        }
    }
}

```