パソコン甲子園2

今日は2006年のパソコン甲子園の本選の問題(PDF)の9問目。9でぐるぐるという、なにか作為的なものを感じるけど*1、そうだとしたら記号は#じゃなくて@じゃないと*2

問題の概略は、
「与えられた大きさのぐるぐるを表示するプログラムをつくれ」
というもの。これ、2の時どうしようか。

##
##

とするか、

##
#

とするか。ぐるぐるの条件は、

  • 1辺の長さがnの場合、n行n列の文字列として表示する。
  • 左下隅を基点とし,時計回りに回転する渦状の模様とする。
  • 線のある部分は#(半角シャープ)、空白部分は“ ”(半角空白)で表現する。
  • 線と線の間は空白を置く。
  • nは1以上100以下の整数とする。

となっていて、4番目の「線と線の間は空白を置く」という条件から、下のほうが正しそう。

ってことで、作ってみた。超適当。

import java.io.*;

public class Q09 {
    public static final class Yuyu {
        private static final class Point {
            public final int i;
            public final int j;
            public Point(int i, int j) {
                this.i = i;
                this.j = j;
            }
        }
        
        public static enum Direction {
            TOP {
                Point getStartPos(int size) { return new Point(size - 1, 0); }
                Direction next() { return RIGHT; }
                void move(Yuyu yuyu) { yuyu.top(); }
                Point getNextNext(int i, int j) { return new Point(i-2, j); }
            },
            RIGHT {
                Point getStartPos(int size) { return new Point(0, 0); }
                Direction next() { return BOTTOM; }
                void move(Yuyu yuyu) { yuyu.right(); }
                Point getNextNext(int i, int j) { return new Point(i, j+2); }
            },
            BOTTOM {
                Point getStartPos(int size) { return new Point(0, size - 1); }
                Direction next() { return LEFT; }
                void move(Yuyu yuyu) { yuyu.bottom(); }
                Point getNextNext(int i, int j) { return new Point(i+2, j); }
            },
            LEFT {
                Point getStartPos(int size) { return new Point(size - 1, size - 1); }
                Direction next() { return TOP; }
                void move(Yuyu yuyu) { yuyu.left(); }
                Point getNextNext(int i, int j) { return new Point(i, j-2); }
            };
            abstract Point getStartPos(int size);
            abstract Direction next();
            abstract void move(Yuyu yuyu);
            abstract Point getNextNext(int i, int j);
        }
        
        private final char ch;
        private final int size;
        
        private int crntI;
        private int crntJ;
        private char[][] field;
        
        private Direction dir;
        
        public Yuyu(char ch, int size, Direction dir) {
            this.ch     = ch;
            this.size   = size;
            this.dir    = dir;
            initField();
        }
        
        private void initField() {
            field = new char[size][size];
            for (int i = 0; i < size; i++) {
                for (int j = 0; j < size; j++) {
                    field[i][j] = ' ';
                }
            }
            Point startPos = dir.getStartPos(size);
            crntI = startPos.i;
            crntJ = startPos.j;
        }
        
        private char getNextNext() {
            Point crnt = dir.getNextNext(crntI, crntJ);
            int i = crnt.i, j = crnt.j;
            // フィールドの外は空白として考える
            if (i == -1 || j == -1 || i == size || j == size)
                return ' ';
            // フィールドの外の外はchとして考える
            if (i == -2 || j == -2 || i == size+1 || j == size+1)
                return ch;
            return field[crnt.i][crnt.j];
        }
        
        public String createYuyu() {
            boolean isFinish = false;
            while (true) {
                field[crntI][crntJ] = ch;
                if (getNextNext() == ' ') {
                    dir.move(this);
                    isFinish = false;
                } else {
                    dir = dir.next();
                    // 方向がすぐに変わってしまうか、行き場所がなければ終了
                    if (isFinish || getNextNext() == ch) break;
                    dir.move(this);
                    isFinish = true;
                }
            }
            
            StringBuilder result = new StringBuilder(size * size + size);
            for (char[] line : field)
                result.append(line).append('\n');
            result.deleteCharAt(result.length() - 1);
            return result.toString();
        }
        
        private void top()    { crntI--; }
        private void right()  { crntJ++; }
        private void bottom() { crntI++; }
        private void left()   { crntJ--; }
    }
    
    public static String createYuyu(int size) {
        // return new Yuyu('#', size, Yuyu.Direction.TOP).createYuyu();
        return new Yuyu('@', size, Yuyu.Direction.LEFT).createYuyu();
    }
    
    public static void main(String[] args) throws IOException {
        BufferedReader in = null;
        try {
            in = new BufferedReader(
                new InputStreamReader(System.in));
            int size = Integer.parseInt(in.readLine());
            System.out.println(createYuyu(size));
        } finally {
            if (in != null) in.close();
        }
    }
}

つか、本当の大会ならこんな悠長なプログラム書いてられないよなぁ。是非どんなコードを書いているのか見てみたい。

あと問題自体には関係ないけど、private staticなclassのメソッドにpublicとか書いてしまうのはもはや癖。

*1:だまれ

*2:いやだからだまれ