Problem:
問題文

お菓子の魔女 CHARLOTTE は巴マミとクッキーゲームを楽しんでいる．クッキーゲームは 8\times 8 の格子状に区切られたテーブルクロスの上にチーズクッキーとチョコレートクッキーを置いて行われる．各格子には高々 1 個のチョコレートクッキーまたはチーズクッキーしか置くことはできない．

お菓子の魔女はチーズクッキーを，巴マミはチョコレートクッキーを交互に置いてゲームを行う．自分のクッキーを置いたあと，そのクッキーから上下左右斜めの各 8 方向について，置くクッキーとすでに置いていた自分のクッキーの間に相手のクッキーのみが直線に並んでいた場合に，その挟まれた相手のクッキーのすべてが自分のクッキーで置き換えられる．クッキーゲームのプレイヤーは自分のターンが回ってきた時，1 つ自分のクッキーを置くことができる．ただし，相手のクッキーを少なくとも 1 つ以上自分のクッキーに置き換えられなければならない．そのような置き場がない場合，自分のターンをパスをしなければならない．

お菓子の魔女も巴マミも考えるのが少々苦手である．そこで，回ってきたターン毎にそのターンの中で置き換えられるクッキーの数を最大化することを考えることにした．巴マミのターンのときに置き換えられるクッキーの数を最大にするようなクッキーを置く場所の候補が複数ある場合は，より上の場所を，それでも複数ある場合はより左の場所を選択することにした．また同様に，お菓子の魔女のターンのときに候補が複数ある場合はより下の場所を，それでも複数ある場合はより右の場所を選択することにした．

テーブルクロスに置かれたクッキーの状態が与えられるので，巴マミからはじめ，彼女たちがそこからクッキーゲームを行い，共に新たなクッキーが置けなくなるまでゲームを続けた時のテーブルクロスの上に置かれたクッキーの状態を求めよ．

入力形式

入力は以下の形式で与えられる．

s_{11} s_{12} ... s_{18}\\
s_{21} s_{22} ... s_{28}\\
...\\
s_{81} s_{82} ... s_{88}\\

s_{ij} はテーブルクロスに置かれたクッキーの初期状態を表す文字で，上から i 行目，左から j 列目の格子の状態を表す．チョコレートクッキーが置かれているときs_{ij}は 'o' であり，チーズクッキーが置かれているときは 'x', 何も置かれていないときは '.' となる．

出力形式

クッキーゲームが行われた後のテーブルクロスの上に置かれたクッキーの状態を，入力形式と同じ形式で出力せよ．

制約

s_{ij} は 'o', 'x', '.' のいずれかである．

入出力例

入力例 1

ooox....
.x......
ooo.....
........
........
........
........
........

出力例1

ooooo...
.o......
ooo.....
...o....
....o...
........
........
........

入力例 2

........
........
........
...ox...
...xo...
........
........
........

出力例 2

xxxxxxxx
xxxooxxx
xxxxooxx
xxxxxxxx
ooxxooox
ooxoooox
oxooooox
ooooooox

入力例 3

........
........
..ooo...
..oxo...
..ooo...
........
........
........

出力例 3

........
........
..ooo...
..ooo...
..ooo...
.....o..
......o.
........

Problem Setter: Flat35

Solution in C# (judge verdict: Accepted)

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

namespace Dessert_Witch
{
    class Program
    {
        static void Main(string[] args)
        {
            Field field = new Field();
            for (int i = 0; i < Field.SIZE; i++)
            {
                char[] fieldChars = Console.ReadLine().ToCharArray();
                for (int j = 0; j < Field.SIZE; j++)
                {
                    if (fieldChars[j] == '.') field.SetCookie(j, i, Cookie.None);
                    if (fieldChars[j] == 'o') field.SetCookie(j, i, Cookie.Choco);
                    if (fieldChars[j] == 'x') field.SetCookie(j, i, Cookie.Cheese);
                }
            }

            Game game = new Game(field);
            game.Play();
            game.Disp();
        }
    }
}

    public class Game
    {
        private Field field;
        private int[] rotateX = new int[] { 0, 1, 1, 1, 0, -1, -1, -1};
        private int[] rotateY = new int[] { -1, -1, 0, 1, 1, 1, 0, -1 };

        public Game(Field field)
        {
            this.field = field;
        }


        public void Play()
        {
            Player mami = new Mami();
            Player charlotte = new Charlotte();
            while (true){
                bool mamiPlayable = Play(mami);
                bool charlottePlayable = Play(charlotte);
                if (!mamiPlayable && !charlottePlayable) break;
            }
        }

        public void Disp()
        {
            Console.Write(field.Disp());
        }


        private bool Play(Player player)
        {
            // クッキーを置く位置を決定
            Point point = player.Decide(field);

            // 位置が決定できなかったので終了
            if (point == null) return false;

            field.SetCookie(point.X, point.Y, player.GetMyCookie());

            for (int way = 0; way < rotateX.Length; way++)
            {
                int count = 1;
                bool isBetween = false;
                for (int i = 1; i < Field.SIZE; i++)
                {
                    Cookie cookie = field.GetCookie(point.X + (rotateX[way] * i), point.Y + (rotateY[way] * i));
                    if (cookie == Cookie.None) break;
                    if (cookie == player.GetMyCookie())
                    {
                        isBetween = true;
                        break;
                    }
                    else count++;
                }
                if (!isBetween) continue;
               
                for (int i = 1; i < count; i++)
                {
                    field.SetCookie(point.X + (rotateX[way] * i), point.Y + (rotateY[way] * i), player.GetMyCookie());
                }                
            }
            return true;
        }
    }

    public enum Cookie
    {
        None,
        Choco,
        Cheese
    }

    public class Field
    {
        public const int SIZE = 8;

        private Cookie[,] field;

        public Field()
        {
            field = new Cookie[SIZE, SIZE];
            for (int i = 0; i < SIZE; i++)
            {
                for (int j = 0; j < SIZE; j++)
                {
                    field[i, j] = Cookie.None;
                }
            }
        }

        public void SetCookie(int x, int y, Cookie cookie)
        {
            if (x < 0 || x >= SIZE || y < 0 || y >= SIZE) return;
            field[x, y] = cookie;
        }

        public Cookie GetCookie(int x, int y)
        {
            if (x < 0 || x >= SIZE || y < 0 || y >= SIZE) return Cookie.None;
            return field[x, y];
        }

        public String Disp()
        {
            string str = "";
            for (int y = 0; y < SIZE; y++)
            {
                for (int x = 0; x < SIZE; x++)
                {
                    if (field[x, y] == Cookie.None) str += ".";
                    if (field[x, y] == Cookie.Cheese) str += "x";
                    if (field[x, y] == Cookie.Choco) str += "o";
                }
                str += "\n";
            }
            return str;
        }
        
    }

    public class Point
    {

        public int X { get; private set; }

        public int Y { get; private set; }

        public Point(int x, int y)
        {
            X = x;
            Y = y;
        }
    }

    public abstract class Player
    {

        private int[] rotateX = new int[] { 0, 1, 1, 1, 0, -1, -1, -1 };
        private int[] rotateY = new int[] { -1, -1, 0, 1, 1, 1, 0, -1 };

        public abstract Cookie GetMyCookie();

        public Point Decide(Field field)
        {
            // 盤面の評価

            int[,] evalField = new int[Field.SIZE, Field.SIZE];

            for (int i = 0; i < Field.SIZE; i++)
            {
                for (int j = 0; j < Field.SIZE; j++)
                {
                    evalField[i, j] = GetEval(field, i, j);
                }
            }
            return GetDecidePoint(evalField);
        }

        private int GetEval(Field field, int x, int y)
        {
            // クッキーがすでにある場合、クッキーを置けないので評価値は0
            if (field.GetCookie(x, y) != Cookie.None) return 0;

            int eval = 0;
            for (int i = 0; i < rotateX.Length; i++)
            {
                int count = 0;
                for (int j = 1; j < Field.SIZE; j++)
                {
                    Cookie cookie = field.GetCookie(x + (rotateX[i] * j), y + (rotateY[i] * j));
                    if (cookie == Cookie.None) break;
                    if (cookie == GetMyCookie())
                    {
                        eval += count;
                        break;
                    }
                    if (cookie != GetMyCookie()) count++;
                }
            }
            return eval;
        }

        private Point GetDecidePoint(int[,] evalField)
        {
            Point maxPoint = null;
            int maxEval = 0;
 
            for (int y = 0; y < Field.SIZE; y++)
            {
                for (int x = 0; x < Field.SIZE; x++)
                {
                    if (evalField[x, y] == 0) continue;
                    if (maxEval < evalField[x, y])
                    {
                        maxEval = evalField[x, y];
                        maxPoint = new Point(x, y);
                    }
                    if (maxEval == evalField[x, y])
                    {
                        maxPoint = OnDecide(maxPoint, new Point(x, y));
                    }
                }
            }
            return maxPoint;
        }

        protected abstract Point OnDecide(Point a, Point b);
    }

    public class Mami : Player
    {
        public override Cookie GetMyCookie()
        {
            return Cookie.Choco;
        }

        protected override Point OnDecide(Point a, Point b)
        {
            /*
             * 同じ評価の場合、マミさんは上の場所、それでもダメなら左の場所
             */
            if (a.Y < b.Y) return a;
            else if (a.Y > b.Y) return b;
            else
            {
                if (a.X < b.X) return a;
                else return b;
            }
        }
    }

    public class Charlotte : Player
    {
        public override Cookie GetMyCookie()
        {
            return Cookie.Cheese;
        }

        protected override Point OnDecide(Point a, Point b)
        {
            /*
             * 同じ評価の場合、シャルロッテは下の場所、それでもダメなら右の場所
             */
            if (a.Y > b.Y) return a;
            else if (a.Y < b.Y) return b;
            else
            {
                if (a.X > b.X) return a;
                else return b;
            }
        }
    }