第五回オフラインリアルタイムどう書くの参考問題の実装例(java)

オフラインでリアルタイムに「どう書く」をやるイベント。
http://atnd.org/events/32979
の参考問題
http://nabetani.sakura.ne.jp/hena/ord5railsontiles/ ( http://qiita.com/items/0ddde0164a745cd09c34 )
の実装。

だれも java で書いてくれないので java で。

テストを走らせるクラスと、実装クラスの２つで。

```java:RailsOnTiles.java
import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 */
public class RailsOnTiles {
  public enum Direction {
    Up, Down, Right, Left
  }

  public enum Tile{
    Straight,
    TypeS,
    TypeZ;
    public static Tile fromChar( char ch ) {
      switch( ch ){
        case '0': return Straight;
        case '1': return TypeS;
        case '2': return TypeZ;
      }
      return null;
    }
  }

  List<Tile> board;

  public RailsOnTiles(String input) {
    board = createBoard(input);
  }

  private List<Tile> createBoard(String input) {
    List<Tile> result = new ArrayList<Tile>(9);
    for( int i=0 ; i<9 ; ++i ){
      char name = input.charAt(i);
      result.add( Tile.fromChar(name) );
    }
    return result;
  }
  static char lastOf( StringBuffer s )
  {
    return s.charAt( s.length()-1 );
  }
  public String solve(char start, Direction dir) {
    StringBuffer result = new StringBuffer(18);
    result.append( start );
    for(;;){
      char curPos = lastOf( result );
      dir = turn( dir, tileAt(curPos) );
      Character nextPos = moveOnRails(curPos, dir);
      if ( nextPos==null ){
        break;
      }
      result.append( nextPos.charValue() );
    }
    return result.toString();
  }

  private Tile tileAt(char curPos) {
    int index = curPos-'A';
    if ( 0<=index && index<board.size()){
      return board.get(index);
    } else {
      return null;
    }
  }

  private Character moveOnRails(char curPos, Direction dir) {
    switch( dir ){
      case Up:    return moveImpl( curPos, 0, -1 );
      case Down:    return moveImpl( curPos, 0, 1 );
      case Right:    return moveImpl( curPos, 1, 0 );
      case Left:    return moveImpl( curPos, -1, 0 );
    }
    assert( false );
    return null;
  }

  private Character moveImpl(char curPos, int dx, int dy) {
    int ipos = curPos - 'A';
    int x0 = ipos%3;
    int y0 = ipos/3;
    int x1 = x0+dx;
    int y1 = y0+dy;
    if ( 0<=x1 && x1<3 && 0<=y1 && y1<3 ){
      return (char) ('A' + x1 + y1 * 3);
    } else {
      return null;
    }
  }

  private Direction turn(Direction dir, Tile t) {
    switch( t ){
      case Straight: return dir;
      case TypeS:
        switch( dir ){
          case Up:return Direction.Left;
          case Left:return Direction.Up;
          case Down:return Direction.Right;
          case Right:return Direction.Down;
        }
        break; // unreachable.
      case TypeZ:
        switch( dir ){
          case Up:return Direction.Right;
          case Right:return Direction.Up;
          case Down:return Direction.Left;
          case Left:return Direction.Down;
        }
        break; // unreachable.
    }
    assert false;
    return null;
  }
}
```

続いてテストランナー。

```java:RailsOnTiles.java
/**
 * Created with IntelliJ IDEA.
 */
public class TestRunner {
  static void runTest( String input, String expected )
  {
    RailsOnTiles target = new RailsOnTiles( input );
    String actual = target.solve( 'B', RailsOnTiles.Direction.Down );
    if (actual.equals( expected )){
      System.out.printf( "%s->%s\n", input, actual );
    } else {
      System.err.printf( "%s->%s : expected is %s\n", input, actual, expected );
    }
  }
  public static void main( String[] args )
  {
    runTest( "101221102", "BEDGHIFEH" );
    runTest( "000000000", "BEH" );
  }
}
```

例によって、テストデータはほとんど省略。

javaだからenum にしないととか思いながらだらだら書いたら 100行を軽く超えた。
これを書いた最大の収穫は、enum をあんまりわかってないことがわかったという点。
勉強します。

あと。
Qiita のタグに書く java のバージョンって、1.7 がいいのか 7 がいいのか悩む。
1.7 にしておいた。