Codingame『Bender, a depressed robot』クリア


 永遠のループに陥るか、それとも自殺するかの救われないストーリーである。


 何が悩んだかってループ判定ですよ、ループ判定。
上手くいくまで両手の関節の数くらいは心折れたと思う。

using System;
using System.Linq;
using System.IO;
using System.Text;
using System.Collections;
using System.Collections.Generic;

/**
 * Auto-generated code below aims at helping you parse
 * the standard input according to the problem statement.
 **/
class Solution
{
    public static Bender ben;

    static void Main(string[] args)
    {
        //勉三さんの生成
        ben = new Bender();

        //地図の表示
        ben.ShowMap();

        //ループ判定
        bool loop = true;

        //ゲームループ
        while (loop)
        {
            //勉三さんの死orループ判定
            loop = !ben.PonkotsuAI();
        }

        //勉三さんの歴史を語る
        ben.TalkHistory();
    }



    /// <summary>
    /// 勉三さんクラス
    /// </summary>
    public class Bender
    {
        //地図
        private Map map;

        //移動履歴
        private List<string> MoveRecord;

        //方向(文字)と移動方向(座標)をくっつけたもの
        private Dictionary<string, Point> Dic = new Dictionary<string, Point>()
        {
            {"SOUTH", new Point(0, 1)},
            {"EAST", new Point(1, 0)},
            {"NORTH", new Point(0, -1)},
            {"WEST", new Point(-1, 0)},
        };

        //移動方向
        private string[] Way = { "SOUTH", "EAST", "NORTH", "WEST" };

        //現在の座標
        private Point Coordinate;
        //現在の移動方向
        private string Direction;

        //破壊者モードフラグ
        private bool Breaker;
        //壊したブロックの数
        private int BlockBreakNum;

        //リバースモードフラグ
        private bool Reverse;

        //生存フラグ
        private bool Live;
        //ループフラグ
        private bool Loop;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Bender()
        {
            this.map = new Map();
            this.MoveRecord = new List<string>();
            //地図のスタートポイントを設定
            this.Coordinate = map.StartPoint;
            //最初は南向き
            this.Direction = "SOUTH";

            this.Breaker = false;
            this.BlockBreakNum = 0;

            this.Reverse = false;
            this.Live = true;

            this.Loop = false;
        }

        /// <summary>
        /// ポンコツ頭脳
        /// </summary>
        /// <returns>bool : 終了か続行か</returns>
        public bool PonkotsuAI()
        {
            //移動方向の決定
            this.DetermineDirection();
            //移動
            this.Move();
            //シンボルの反応
            this.LookSymbol();
            //ループのチェック
            this.CheckLoop();

            //終了か続行か
            return this.IsEnd();
        }

        /// <summary>
        /// 移動方向の決定
        /// </summary>
        public void DetermineDirection()
        {
            //現在方向の一歩先を見る
            char c = map.GetSymbol(ben.Coordinate + ben.Dic[ben.Direction]);
            //障害物アリなら移動方向の変更
            if (ben.isObstacles(c) == true) this.SearchAround(ben.Coordinate, 1);
        }

        /// <summary>
        /// シンボルの反応
        /// </summary>
        public void LookSymbol()
        {
            //現在位置のシンボルを得る
            char c = map.GetSymbol(ben.Coordinate);

            switch (c)
            {
                case 'X':
                    //ブロックの破壊
                    this.BreakObjectX();
                    break;
                case 'B':
                    //破壊者モード on <=> off
                    this.Breaker = !this.Breaker;
                    break;
                case 'I':
                    //リバースモード on <=> off
                    this.Reverse = !this.Reverse;
                    break;
                case '$':
                    //自殺
                    this.Live = false;
                    break;
                case 'S':
                    //南へ
                    this.Direction = "SOUTH";
                    break;
                case 'E':
                    //東へ
                    this.Direction = "EAST";
                    break;
                case 'N':
                    //北へ。White Illumination
                    this.Direction = "NORTH";
                    break;
                case 'W':
                    //西へ
                    this.Direction = "WEST";
                    break;
                case 'T':
                    // *おおっと! テレポーター*
                    this.OnTeleport();
                    break;
            }
        }

        /// <summary>
        /// 移動
        /// </summary>
        public void Move()
        {
            //履歴を記録
            this.WriteHistory();
            //方向に従って座標を移動
            this.Coordinate += this.Dic[this.Direction];
        }

        /// <summary>
        /// 障害物判定
        /// </summary>
        /// <param name="sbl">char : 地図上のシンボル</param>
        /// <returns>bool : true障害物 false障害ではない</returns>
        public bool isObstacles(char sbl)
        {
            switch (sbl)
            {
                case '#':
                    //壁
                    return true;
                case 'X':
                    //ブロックは破壊者モードなら無視
                    if (this.Breaker != true) return true;
                    else return false;
            }

            //壁やブロック以外は障害物ではない
            return false;
        }

        /// <summary>
        /// ブロックの破壊
        /// </summary>
        private void BreakObjectX()
        {
            //地図の書き換え
            this.map.Chips[this.Coordinate.X, this.Coordinate.Y] = ' ';
            //ブロックの破壊数を+1
            this.BlockBreakNum++;
        }

        /// <summary>
        /// テレポーターの上
        /// </summary>
        private void OnTeleport()
        {
            //別のテレポーターに移動
            if (Point.Equal(this.Coordinate, this.map.Telepoter[0]) == true) this.Coordinate = this.map.Telepoter[1];
            else if (Point.Equal(this.Coordinate, this.map.Telepoter[1]) == true) this.Coordinate = this.map.Telepoter[0];
            else Console.Error.WriteLine("Teleport error!");    //石の中にいる
        }

        /// <summary>
        /// 移動可能かどうか周囲を探索
        /// </summary>
        /// <param name="p">Point : 座標</param>
        /// <param name="i">int : 再帰回数制限</param>
        /// <returns>char : 周囲のシンボル</returns>
        private char SearchAround(Point p, int i)
        {
            //シンボルを返す
            if (i == 0) return map.GetSymbol(p);

            //シンボル
            char c;
            //探索する順番
            int start, end, cre;

            //探索する順番を決める
            this.SetOrder(out start, out end, out cre);

            for (int j = start; j != end; j += cre)
            {
                //シンボルを取得
                c = this.SearchAround(p + this.Dic[this.Way[j]], i - 1);
                
                //障害物でなければ、その方向に移動方向を設定
                if (ben.isObstacles(c) == false)
                {
                    this.Direction = this.Way[j];
                    break;
                }
            }

            //とりあえず
            return ' ';
        }

        /// <summary>
        /// 周囲を探索する順番を設定する
        /// </summary>
        /// <param name="start">out int : 開始番号</param>
        /// <param name="end">out int : 終了番号</param>
        /// <param name="cre">out int : 増減加減</param>
        private void SetOrder(out int start, out int end, out int cre)
        {
            if (ben.Reverse == true)
            {
                //リバースモードなら逆順
                start = this.Way.Length - 1;
                end = 0;
                cre = -1;
            }
            else {
                //リバースモードでないなら普通に
                start = 0;
                end = this.Way.Length - 1;
                cre = 1;
            }
        }

        /// <summary>
        /// 勉三さん、履歴を語る
        /// </summary>
        public void TalkHistory()
        {
            //ループしてなければ
            if (this.Loop == false)
            {
                //移動履歴から方向だけ抽出して出力
                foreach (string s in this.MoveRecord)
                {
                    string[] d = s.Split(' ');
                    Console.WriteLine(d[0]);
                }
            }
            else {
                //ループしていればLOOPを出力
                Console.WriteLine("LOOP");
            }
        }

        /// <summary>
        /// ループチェッカー
        /// </summary>
        private void CheckLoop()
        {
            //現在のステータスと同じ状態が履歴にあればループと判定
            if (this.MoveRecord.Exists(x => x == this.NowStatus()) == true)
            {
                this.Loop = true;
            }
        }

        /// <summary>
        /// 終了判定
        /// </summary>
        /// <returns>bool : true終了 false続行</returns>
        private bool IsEnd()
        {
            //死ぬかループしてたらtrue、それ以外はfalse
            if (this.Live == false | this.Loop == true) return true;
            else return false;
        }

        /// <summary>
        /// 履歴を書き込む
        /// </summary>
        public void WriteHistory()
        {
            //現在のステータスを記録
            this.MoveRecord.Add(this.NowStatus());
        }

        /// <summary>
        /// 現在のステータス
        /// </summary>
        /// <returns>string : 現在のステータス</returns>
        private string NowStatus()
        {
            return string.Format("{0} {1}{2}{3}{4}", this.Direction, this.Coordinate, this.Breaker, this.Reverse, this.BlockBreakNum);
        }

        /// <summary>
        /// マップの表示
        /// </summary>
        public void ShowMap()
        {
            this.map.ShowMap();
        }
    }

    /// <summary>
    /// 座標クラス
    /// </summary>
    public class Point
    {
        public int X;
        public int Y;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="x">x座標</param>
        /// <param name="y">y座標</param>
        public Point(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }

        /// <summary>
        /// 2つの座標が等しいか調べる
        /// </summary>
        /// <param name="p1">Point : 比較対象1</param>
        /// <param name="p2">Point : 比較対象2</param>
        /// <returns>bool : true等しい false等しくない</returns>
        public static bool Equal(Point p1, Point p2)
        {
            if (p1.X == p2.X & p1.Y == p2.Y) return true;
            else return false;
        }

        /// <summary>
        /// +演算子
        /// </summary>
        /// <param name="p1">Point : 足すやつ</param>
        /// <param name="p2">Point : 足すやつ</param>
        /// <returns>Point : コンゴトモヨロシク</returns>
        public static Point operator +(Point p1, Point p2)
        {
            return new Point(p1.X + p2.X, p1.Y + p2.Y);
        }

        /// <summary>
        /// 文字列にする
        /// </summary>
        /// <returns>string : 座標の文字列</returns>
        public override string ToString()
        {
            return string.Format("{0} {1}", this.X, this.Y);
        }

    }

    /// <summary>
    /// 地図クラス
    /// </summary>
    public class Map
    {
        //地図本体
        public char[,] Chips;

        //縦幅
        public int Width;
        //横幅
        public int Height;

        //スタート座標
        public Point StartPoint;
        //テレポーター座標
        public Point[] Telepoter = new Point[2];

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Map()
        {
            //地図を作る
            this.CreateMap();
            //シンボルを読み込む
            this.GetMapSymbols();
        }

        /// <summary>
        /// 地図を作る
        /// </summary>
        private void CreateMap()
        {
            string[] inputs = Console.ReadLine().Split(' ');
            this.Height = int.Parse(inputs[0]);
            this.Width = int.Parse(inputs[1]);
            this.Chips = new char[this.Width, this.Height];
        }

        /// <summary>
        /// シンボルを読み込む
        /// </summary>
        private void GetMapSymbols()
        {
            for (int y = 0; y < this.Height; y++)
            {
                string row = Console.ReadLine();
                for (int x = 0; x < this.Width; x++)
                {
                    //地図にシンボルを書き込む
                    this.Chips[x, y] = row[x];

                    //スタート位置を発見
                    if (row[x] == '@')
                    {
                        this.StartPoint = new Point(x, y);
                    }
                    //テレポーターを発見
                    if (row[x] == 'T')
                    {
                        if (this.Telepoter[0] == null) this.Telepoter[0] = new Point(x, y);
                        else this.Telepoter[1] = new Point(x, y);
                    }
                }
            }
        }

        /// <summary>
        /// シンボルを取得する
        /// </summary>
        /// <param name="p">Point : 座標</param>
        /// <returns>char : シンボル</returns>
        public char GetSymbol(Point p)
        {
            return this.Chips[p.X, p.Y];
        }

        /// <summary>
        /// 地図を表示
        /// </summary>
        public void ShowMap()
        {
            Console.Error.WriteLine("***Symbols Map***");
            for (int y = 0; y < this.Height; y++)
            {
                for (int x = 0; x < this.Width; x++)
                {
                    Console.Error.Write(this.Chips[x, y]);
                }
                Console.Error.WriteLine("");
            }
        }
    }
}


めいろでめちゃめちゃあそぶっく!

めいろでめちゃめちゃあそぶっく!