めっちゃ時間かかった。自分才能なさすぎ。コード量多すぎ。
ロジックは以下。
- インプットの情報で積み木の水槽を作る
- とりあえず全部水で満たす
- 右左下のいずれかに移動できる水を削除
3番目のステップを、削除できる水が無くなるまで繰り返す。
実装
起動用クラス
BlockTank.java
package hoge;
import java.util.ArrayList;
import java.util.List;
public class BlockTank {
public static void main(String[] args) {
new BlockTank().execute(args[0]);
}
public void execute(String arg) {
// 引数解析
List<Integer> design = new ArrayList<>();
for (int i=0; i<arg.length(); i++) {
design.add(Integer.valueOf(arg.substring(i, i+1)));
}
Field field = new Field(design);
field.spillWater();
int amount = field.getAmountOfWater();
// 結果表示
System.out.println(field);
System.out.println(amount);
}
}
積み木を置くフィールド
Field.java
package hoge;
import static hoge.Cell.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Field {
private int LEFT_EDGE = 0;
private int RIGHT_EDGE;
private int BOTTOM_EDGE = 0;
private Cell[][] field;
private int width;
private int height;
public Field(List<Integer> design) {
this.initMatrix(design);
this.fillWater();
this.putBlocks(design);
}
private void initMatrix(List<Integer> design) {
width = design.size();
height = Collections.max(design);
field = new Cell[width][height];
RIGHT_EDGE = width - 1;
}
private void fillWater() {
for (Cell[] column : field) {
Arrays.fill(column, WATER);
}
}
private void putBlocks(List<Integer> design) {
for (int x=0; x<design.size(); x++) {
int wallHeight = design.get(x);
for (int y=0; y<wallHeight; y++) {
field[x][y] = BLOCK;
}
}
}
public void spillWater() {
boolean remove;
do {
remove = forEachFieldCell(new CallBack<Boolean>() {
private boolean remove;
@Override public void visit(int x, int y, Cell cell) {
if (cell == WATER) {
if (isEdge(x, y) || existsBlankAround(x, y)) {
field[x][y] = BLANK;
remove = true;
}
}
}
@Override public Boolean result() {
return remove;
}
});
} while(remove);
}
private boolean existsBlankAround(int x, int y) {
Cell left = field[x-1][y];
Cell right = field[x+1][y];
Cell bottom = field[x][y-1];
return left == BLANK || right == BLANK || bottom == BLANK;
}
private boolean isEdge(int x, int y) {
return x == LEFT_EDGE || x == RIGHT_EDGE || y == BOTTOM_EDGE;
}
public int getAmountOfWater() {
return forEachFieldCell(new CallBack<Integer>() {
private int amount = 0;
@Override public void visit(int x, int y, Cell cell) {
if (cell == WATER) {
amount++;
}
}
@Override public Integer result() {
return amount;
}
});
}
private <R> R forEachFieldCell(CallBack<R> callback) {
for (int x=0; x<width; x++) {
for (int y=0; y<height; y++) {
callback.visit(x, y, field[x][y]);
}
}
return callback.result();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (int y=height-1; 0<=y; y--) {
for (int x=0; x<width; x++) {
sb.append(field[x][y]);
}
if (y != 0) {
sb.append(System.getProperty("line.separator"));
}
}
return sb.toString();
}
private static interface CallBack<R> {
void visit(int x, int y, Cell cell);
R result();
}
}
フィールドに置けるもの
Cell.java
package hoge;
public enum Cell {
BLANK(" "),
BLOCK("■"),
WATER("水"),
;
private String value;
private Cell(String value) {
this.value = value;
}
@Override
public String toString() {
return this.value;
}
}
JUnit
BlockTankTest.java
package hoge;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import hoge.BlockTank;
import java.io.PrintStream;
import org.junit.Before;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
@RunWith(Theories.class)
public class BlockTankTest {
@DataPoints
public static Fixture[] fixtures = {
new Fixture() {{
input = "83141310145169154671122";
expected = 24;
}},
new Fixture() {{
input = "923111128";
expected = 45;
}},
new Fixture() {{
input = "923101128";
expected = 1;
}},
new Fixture() {{
input = "903111128";
expected = 9;
}},
new Fixture() {{
input = "3";
expected = 0;
}},
new Fixture() {{
input = "31";
expected = 0;
}},
new Fixture() {{
input = "412";
expected = 1;
}},
new Fixture() {{
input = "3124";
expected = 3;
}},
new Fixture() {{
input = "11111";
expected = 0;
}},
new Fixture() {{
input = "222111";
expected = 0;
}},
new Fixture() {{
input = "335544";
expected = 0;
}},
new Fixture() {{
input = "1223455321";
expected = 0;
}},
new Fixture() {{
input = "000";
expected = 0;
}},
new Fixture() {{
input = "000100020003121";
expected = 1;
}},
new Fixture() {{
input = "1213141516171819181716151413121";
expected = 56;
}},
new Fixture() {{
input = "712131415161718191817161514131216";
expected = 117;
}},
new Fixture() {{
input = "712131405161718191817161514031216";
expected = 64;
}},
new Fixture() {{
input = "03205301204342100";
expected = 1;
}},
new Fixture() {{
input = "0912830485711120342";
expected = 18;
}},
new Fixture() {{
input = "1113241120998943327631001";
expected = 20;
}},
new Fixture() {{
input = "7688167781598943035023813337019904732";
expected = 41;
}},
new Fixture() {{
input = "2032075902729233234129146823006063388";
expected = 79;
}},
new Fixture() {{
input = "8323636570846582397534533";
expected = 44;
}},
new Fixture() {{
input = "2142555257761672319599209190604843";
expected = 41;
}},
new Fixture() {{
input = "06424633785085474133925235";
expected = 51;
}},
new Fixture() {{
input = "503144400846933212134";
expected = 21;
}},
new Fixture() {{
input = "1204706243676306476295999864";
expected = 21;
}},
new Fixture() {{
input = "050527640248767717738306306596466224";
expected = 29;
}},
new Fixture() {{
input = "5926294098216193922825";
expected = 65;
}},
new Fixture() {{
input = "655589141599534035";
expected = 29;
}},
new Fixture() {{
input = "7411279689677738";
expected = 34;
}},
new Fixture() {{
input = "268131111165754619136819109839402";
expected = 102;
}},
};
@Before
public void setup() {
System.setOut(new PrintStream(System.out) {
@Override
public void println(int i) {
actual = i;
System.out.println("amount of water = " + i);
}
});
}
private int actual;
@Theory
public void test(Fixture fixture) {
System.out.println("input = " + fixture.input);
// exercise
new BlockTank().execute(fixture.input);
// verify
assertThat(fixture.input, actual, is(fixture.expected));
System.out.println();
}
private static class Fixture {
public String input;
public int expected;
}
}
単純なコールバック関数が欲しいときに Java の限界を感じた。