diff --git a/src/main/java/Ladder.java b/src/main/java/Ladder.java deleted file mode 100644 index 0d2b070..0000000 --- a/src/main/java/Ladder.java +++ /dev/null @@ -1,8 +0,0 @@ -public class Ladder { - - private final int[][] rows; - - public Ladder(int row, int numberOfPerson) { - rows = new int[row][numberOfPerson]; - } -} diff --git a/src/main/java/ladder/Direction.java b/src/main/java/ladder/Direction.java new file mode 100644 index 0000000..da92119 --- /dev/null +++ b/src/main/java/ladder/Direction.java @@ -0,0 +1,16 @@ +package ladder; + +public enum Direction { + + LEFT(-1), RIGHT(1), NONE(0); + + private final int value; + + Direction(int value) { + this.value = value; + } + + public int getValue() { + return value; + } +} diff --git a/src/main/java/ladder/ExceptionMessage.java b/src/main/java/ladder/ExceptionMessage.java new file mode 100644 index 0000000..8ff7aae --- /dev/null +++ b/src/main/java/ladder/ExceptionMessage.java @@ -0,0 +1,20 @@ +package ladder; + +public enum ExceptionMessage { + + INVALID_LADDER_POSITION("사다리 위치는 1이상 자연수입니다."), + INVALID_LADDER_NUMBER("사다리의 행과 열은 2 이상이어야 합니다."), + INVALID_POSITION("유효하지 않은 위치입니다."), + INVALID_DRAW_POSITION("사다리를 그릴 수 없는 위치입니다."), + INVALID_NATURAL_NUMBER("자연수가 아닙니다."); + + private final String message; + + ExceptionMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/ladder/GreaterThanOne.java b/src/main/java/ladder/GreaterThanOne.java new file mode 100644 index 0000000..73293ca --- /dev/null +++ b/src/main/java/ladder/GreaterThanOne.java @@ -0,0 +1,24 @@ +package ladder; + +public class GreaterThanOne { + private final int number; + + private GreaterThanOne(int number) { + this.number = number; + } + + public static GreaterThanOne from(int number){ + if(!isGreaterThanOne(number)){ + throw new IllegalArgumentException(ExceptionMessage.INVALID_LADDER_NUMBER.getMessage()); + } + return new GreaterThanOne(number); + } + + private static boolean isGreaterThanOne(int number) { + return number > 1; + } + + public int getNumber() { + return number; + } +} diff --git a/src/main/java/ladder/LadderGame.java b/src/main/java/ladder/LadderGame.java new file mode 100644 index 0000000..5203ddf --- /dev/null +++ b/src/main/java/ladder/LadderGame.java @@ -0,0 +1,26 @@ +package ladder; + +import ladder.creator.LadderCreator; + +public class LadderGame { + + private final LadderCreator ladderCreator; + + private LadderGame(LadderCreator ladderCreator) { + this.ladderCreator = ladderCreator; + } + + public static LadderGame from(LadderCreator ladderCreator){ + return new LadderGame(ladderCreator); + } + + public int run(Position position) { + LadderRunner ladderRunner = LadderRunner.from(ladderCreator.getLadderWrapper()); + ladderRunner.run(position); + return position.getValue(); + } + + public LadderCreator getLadderCreator() { + return ladderCreator; + } +} \ No newline at end of file diff --git a/src/main/java/ladder/LadderGameFactory.java b/src/main/java/ladder/LadderGameFactory.java new file mode 100644 index 0000000..7646ede --- /dev/null +++ b/src/main/java/ladder/LadderGameFactory.java @@ -0,0 +1,17 @@ +package ladder; + +import ladder.creator.CustomLadderCreator; +import ladder.creator.LadderCreator; +import ladder.creator.RandomLadderCreator; + +public class LadderGameFactory { + public static LadderGame createRandomLadderGame(LadderSize ladderSize){ + LadderCreator randomLadderCreator = RandomLadderCreator.from(ladderSize); + return LadderGame.from(randomLadderCreator); + } + + public static LadderGame createCustomLadderGame(LadderSize ladderSize){ + LadderCreator customLadderCreator = CustomLadderCreator.from(ladderSize); + return LadderGame.from(customLadderCreator); + } +} diff --git a/src/main/java/ladder/LadderPosition.java b/src/main/java/ladder/LadderPosition.java new file mode 100644 index 0000000..05580c0 --- /dev/null +++ b/src/main/java/ladder/LadderPosition.java @@ -0,0 +1,23 @@ +package ladder; + +public class LadderPosition { + private Position rowPos; + private Position colPos; + + private LadderPosition(Position rowPos, Position colPos) { + this.rowPos = rowPos; + this.colPos = colPos; + } + + public static LadderPosition of(Position rowPos, Position colPos){ + return new LadderPosition(rowPos, colPos); + } + + public Position getRowPos() { + return rowPos; + } + + public Position getColPos() { + return colPos; + } +} diff --git a/src/main/java/ladder/LadderRunner.java b/src/main/java/ladder/LadderRunner.java new file mode 100644 index 0000000..44d217d --- /dev/null +++ b/src/main/java/ladder/LadderRunner.java @@ -0,0 +1,30 @@ +package ladder; + +public class LadderRunner { + LadderWrapper ladderWrapper; + + private LadderRunner(LadderWrapper ladderWrapper) { + this.ladderWrapper = ladderWrapper; + } + + public static LadderRunner from(LadderWrapper ladderWrapper) { + return new LadderRunner(ladderWrapper); + } + + public int run(Position position) { + LadderPosition ladderPosition = LadderPosition.of(Position.from(0), position); + while(!ladderWrapper.isLadderPositionAtLastRow(ladderPosition)){ + moveTurnPrinting(ladderPosition); + } + Position lastColPosition = ladderPosition.getColPos(); + return lastColPosition.getValue(); + } + + private void moveTurnPrinting(LadderPosition ladderPosition){ + ladderWrapper.printRowsWithLabel(ladderPosition, "Before"); + ladderWrapper.changeLadderPositionHorizontally(ladderPosition); + ladderWrapper.printRowsWithLabel(ladderPosition, "After"); + ladderWrapper.changeLadderPositionVertically(ladderPosition); + System.out.println(); + } +} diff --git a/src/main/java/ladder/LadderSize.java b/src/main/java/ladder/LadderSize.java new file mode 100644 index 0000000..f395494 --- /dev/null +++ b/src/main/java/ladder/LadderSize.java @@ -0,0 +1,23 @@ +package ladder; + +public class LadderSize { + private GreaterThanOne numberOfRow; + private GreaterThanOne numberOfPerson; + + private LadderSize(GreaterThanOne numberOfRow, GreaterThanOne numberOfPerson){ + this.numberOfRow = numberOfRow; + this.numberOfPerson = numberOfPerson; + } + + public static LadderSize of(GreaterThanOne numberOfRow, GreaterThanOne numberOfPerson){ + return new LadderSize(numberOfRow, numberOfPerson); + } + + public GreaterThanOne getNumberOfRow() { + return numberOfRow; + } + + public GreaterThanOne getNumberOfPerson() { + return numberOfPerson; + } +} diff --git a/src/main/java/ladder/LadderWrapper.java b/src/main/java/ladder/LadderWrapper.java new file mode 100644 index 0000000..643a3e7 --- /dev/null +++ b/src/main/java/ladder/LadderWrapper.java @@ -0,0 +1,62 @@ +package ladder; + +public class LadderWrapper { + private final Row[] rows; + + private LadderWrapper(Row[] rows) { + this.rows = rows; + } + + public static LadderWrapper from(Row[] rows){ + return new LadderWrapper(rows); + } + + public void changeLadderPositionHorizontally(LadderPosition ladderPosition){ + Position rowPosition = ladderPosition.getRowPos(); + rows[rowPosition.getValue()].nextPosition(ladderPosition.getColPos()); + } + + public void changeLadderPositionVertically(LadderPosition ladderPosition){ + Position rowPosition = ladderPosition.getRowPos(); + rowPosition.next(); + } + + public boolean isLadderPositionAtLastRow(LadderPosition ladderPosition){ + Position rowPosition = ladderPosition.getRowPos(); + return rowPosition.getValue() == rows.length; + } + + public void printRowsWithLabel(LadderPosition ladderPosition, String label){ + System.out.println(label); + printRows(ladderPosition); + } + + private void printRows(LadderPosition ladderPosition){ + Position nowRowPosition = ladderPosition.getRowPos(); + for(int rowIndex=0; rowIndex position; + } + + private static void validatePosition(int position) { + if (!isPosition(position)) { + throw new IllegalArgumentException(INVALID_LADDER_POSITION.getMessage()); + } + } + + private static boolean isPosition(int position) { + return position >= 0; + } +} \ No newline at end of file diff --git a/src/main/java/ladder/Row.java b/src/main/java/ladder/Row.java new file mode 100644 index 0000000..2dccbe0 --- /dev/null +++ b/src/main/java/ladder/Row.java @@ -0,0 +1,92 @@ +package ladder; + +import static ladder.Direction.*; + +public class Row { + private final Node[] nodes; + + private Row(GreaterThanOne numberOfPerson) { + nodes = new Node[numberOfPerson.getNumber()]; + for (int i = 0; i < numberOfPerson.getNumber(); i++) { + nodes[i] = Node.from(NONE); + } + } + + public static Row from(GreaterThanOne numberOfPerson){ + return new Row(numberOfPerson); + } + + public StringBuilder buildRowString(){ + StringBuilder stringBuilder = new StringBuilder(); + for(Node node : nodes){ + stringBuilder.append(node.getDirectionValue()).append(" "); + } + return stringBuilder; + } + + public StringBuilder buildStarRowString(Position position){ + StringBuilder stringBuilder = new StringBuilder(); + + for(int colIndex=0; colIndex hashSet = new HashSet<>(); + while (hashSet.size() < maxCreateLine) { + makeRandomLine(hashSet); + } + } + + public LadderPosition makeRandomLine(HashSet hashSet) { + int maxRow = ladderWrapper.getRowsSize(); + int maxCol = ladderWrapper.getColsSize() - 1; + int randomRow = random.nextInt(maxRow); + int randomCol = random.nextInt(maxCol); + LadderPosition ladderPosition = LadderPosition.of(Position.from(randomRow), Position.from(randomCol)); + + try { + drawLine(ladderPosition); + hashSet.add(ladderPosition); + return ladderPosition; + } catch (IllegalArgumentException e) { + return makeRandomLine(hashSet); + } + } + + public int getInLadderValue(LadderPosition ladderPosition){ + return ladderWrapper.getLadderValue(ladderPosition); + } +} diff --git a/src/main/java/ladder/creator/WrapperLadderCreator.java b/src/main/java/ladder/creator/WrapperLadderCreator.java new file mode 100644 index 0000000..86abdb2 --- /dev/null +++ b/src/main/java/ladder/creator/WrapperLadderCreator.java @@ -0,0 +1,29 @@ +package ladder.creator; + +import ladder.*; + +public abstract class WrapperLadderCreator implements LadderCreator { + protected final LadderWrapper ladderWrapper; + + protected WrapperLadderCreator(LadderWrapper ladderWrapper) { + this.ladderWrapper = ladderWrapper; + } + + public LadderWrapper getLadderWrapper() { + return ladderWrapper; + } + + public void drawLine(LadderPosition ladderPosition) { + ladderWrapper.drawLine(ladderPosition); + } + + protected static LadderWrapper createLadderWrapper(LadderSize ladderSize) { + GreaterThanOne numberOfRow = ladderSize.getNumberOfRow(); + GreaterThanOne numberOfPerson = ladderSize.getNumberOfPerson(); + Row[] rows = new Row[numberOfRow.getNumber()]; + for (int i = 0; i < numberOfRow.getNumber(); i++) { + rows[i] = Row.from(numberOfPerson); + } + return LadderWrapper.from(rows); + } +} diff --git a/src/test/java/LadderTest.java b/src/test/java/LadderTest.java deleted file mode 100644 index 741a915..0000000 --- a/src/test/java/LadderTest.java +++ /dev/null @@ -1,5 +0,0 @@ -import static org.junit.jupiter.api.Assertions.*; - -class LadderTest { - -} \ No newline at end of file diff --git a/src/test/java/ladder/LadderGameTest.java b/src/test/java/ladder/LadderGameTest.java new file mode 100644 index 0000000..25907ac --- /dev/null +++ b/src/test/java/ladder/LadderGameTest.java @@ -0,0 +1,161 @@ +package ladder; + +import ladder.creator.CustomLadderCreator; +import ladder.creator.LadderCreator; +import ladder.creator.RandomLadderCreator; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class LadderGameTest { + + @Test + void 사다리_생성_확인() { + //given + GreaterThanOne numberOfRow = GreaterThanOne.from(3); + GreaterThanOne numberOfPerson = GreaterThanOne.from(5); + LadderSize ladderSize = LadderSize.of(numberOfRow, numberOfPerson); + + //when + CustomLadderCreator customLadderCreator = CustomLadderCreator.from(ladderSize); + + //then + assertThat(customLadderCreator).isNotNull(); + } + + @Test + void 사다리_사람_예외_처리_확인() { + //when + GreaterThanOne numberOfRow = GreaterThanOne.from(2); + GreaterThanOne numberOfPerson = GreaterThanOne.from(3); + CustomLadderCreator customLadderCreator = CustomLadderCreator.from(LadderSize.of(numberOfRow, numberOfPerson)); + LadderGame ladderGame = LadderGame.from(customLadderCreator); + + //given + Position position = Position.from(4); + + //then + assertThatThrownBy(() -> ladderGame.run(position)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 사다리_결과_확인() { + //when + GreaterThanOne numberOfRow = GreaterThanOne.from(3); + GreaterThanOne numberOfPerson = GreaterThanOne.from(4); + LadderSize ladderSize = LadderSize.of(numberOfRow, numberOfPerson); + LadderGame ladderGame = LadderGameFactory.createCustomLadderGame(ladderSize); + + LadderCreator ladderCreator = ladderGame.getLadderCreator(); + ladderCreator.drawLine(LadderPosition.of(Position.from(0),Position.from(0))); + ladderCreator.drawLine(LadderPosition.of(Position.from(1),Position.from(1))); + ladderCreator.drawLine(LadderPosition.of(Position.from(2),Position.from(0))); + + //given + Position position = Position.from(0); + + //then + assertThat(ladderGame.run(position)).isEqualTo(2); + + //given + position = Position.from(1); + + //then + assertThat(ladderGame.run(position)).isEqualTo(1); + + //given + position = Position.from(2); + + //then + assertThat(ladderGame.run(position)).isEqualTo(0); + } + + @Test + void 랜덤_사다리_게임_생성_확인() { + //when + GreaterThanOne numberOfRow = GreaterThanOne.from(3); + GreaterThanOne numberOfPerson = GreaterThanOne.from(4); + LadderSize ladderSize = LadderSize.of(numberOfRow, numberOfPerson); + Position position = Position.from(0); + + //given + LadderGame ladderGame1 = LadderGameFactory.createRandomLadderGame(ladderSize); + ladderGame1.run(position); + + LadderGame ladderGame2 = LadderGameFactory.createRandomLadderGame(ladderSize); + ladderGame2.run(position); + + //then + assertThat(ladderGame1).isNotEqualTo(ladderGame2); + } + + @Test + void 랜덤_사다리_게임_결과_확인() { + //when + GreaterThanOne numberOfRow = GreaterThanOne.from(3); + GreaterThanOne numberOfPerson = GreaterThanOne.from(4); + LadderSize ladderSize = LadderSize.of(numberOfRow, numberOfPerson); + LadderGame ladderGame = LadderGameFactory.createRandomLadderGame(ladderSize); + + //given + Position position = Position.from(0); + int result0 = ladderGame.run(position); + + position = Position.from(1); + int result1 = ladderGame.run(position); + + //then + assertThat(result0).isNotEqualTo(result1); + } + + @Test + void 랜덤_사다리_생성_시_주위_노드_정상_여부_확인() { + //when + GreaterThanOne numberOfRow = GreaterThanOne.from(3); + GreaterThanOne numberOfPerson = GreaterThanOne.from(3); + LadderSize ladderSize = LadderSize.of(numberOfRow, numberOfPerson); + RandomLadderCreator ladderCreator = RandomLadderCreator.emptyLadderFrom(ladderSize); + LadderWrapper ladderWrapper = ladderCreator.getLadderWrapper(); + + LadderPosition existPos1 = LadderPosition.of(Position.from(0), Position.from(0)); + ladderCreator.drawLine(existPos1); + LadderPosition existPos2 = LadderPosition.of(Position.from(2), Position.from(1)); + ladderCreator.drawLine(existPos2); + + //given + HashSet existHashSet = new HashSet(List.of(existPos1, existPos2)); + LadderPosition createdLadderPosition = ladderCreator.makeRandomLine(existHashSet); + Position createdRowPosition = createdLadderPosition.getRowPos(); + Position createdColPosition = createdLadderPosition.getColPos(); + + //then + // 중간 열에 라인이 생성되는지 확인 + assertThat(createdLadderPosition.getRowPos().getValue()).isEqualTo(1); + + // createdLadderPosition의 ladder value 값이 1인지 확인 + assertThat(ladderCreator.getInLadderValue(createdLadderPosition)).isEqualTo(1); + + // createdLadderPosition 우측 노드의 ladder value 값이 -1인지 확인 + Position right1Position = createdColPosition.nextPosition(); + LadderPosition right1LadderPosition = LadderPosition.of(createdRowPosition, right1Position); + assertThat(ladderCreator.getInLadderValue(right1LadderPosition)).isEqualTo(-1); + + // createdLadderPosition 2칸 우측 노드의 ladder value 값이 -1이 아닌지 확인 + if(right1Position.getValue() < ladderWrapper.getColsSize() - 1){ + Position right2Position = right1Position.nextPosition(); + LadderPosition right2LadderPosition = LadderPosition.of(createdRowPosition, right2Position); + assertThat(ladderCreator.getInLadderValue(right2LadderPosition)).isNotEqualTo(-1); + } + + // createdLadderPosition 좌측 노드의 ladder value 값이 1이 아닌지 확인 + if(createdColPosition.getValue() > 0) { + Position left1Position = createdColPosition.prevPosition(); + LadderPosition left1LadderPosition = LadderPosition.of(createdRowPosition, left1Position); + assertThat(ladderCreator.getInLadderValue(left1LadderPosition)).isNotEqualTo(1); + } + } +} \ No newline at end of file diff --git a/src/test/java/ladder/NodeTest.java b/src/test/java/ladder/NodeTest.java new file mode 100644 index 0000000..13667cc --- /dev/null +++ b/src/test/java/ladder/NodeTest.java @@ -0,0 +1,43 @@ +package ladder; + +import org.junit.jupiter.api.Test; + +import static ladder.Direction.*; +import static org.assertj.core.api.Assertions.*; + +class NodeTest { + + @Test + void 오른쪽_방향_위치_이동_확인() { + Node node = Node.from(RIGHT); + + Position position = Position.from(0); + + node.move(position); + + assertThat(position.getValue()).isEqualTo(1); + } + + @Test + void NONE_방향_위치_이동_확인() { + Node node = Node.from(NONE); + + Position position = Position.from(0); + + node.move(position); + + assertThat(position.getValue()).isEqualTo(0); + } + + @Test + void 왼쪽_방향_위치_이동_확인() { + Node node = Node.from(LEFT); + + Position position = Position.from(1); + + node.move(position); + + assertThat(position.getValue()).isEqualTo(0); + } + +} \ No newline at end of file diff --git a/src/test/java/ladder/RowTest.java b/src/test/java/ladder/RowTest.java new file mode 100644 index 0000000..615f72f --- /dev/null +++ b/src/test/java/ladder/RowTest.java @@ -0,0 +1,157 @@ +package ladder; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class RowTest { + + @Test + void 한_칸_사다리_이동() { + //when + GreaterThanOne numberOfPerson = GreaterThanOne.from(2); + Row row = Row.from(numberOfPerson); + + //given + Position position = Position.from(0); + row.nextPosition(position); + //then + assertThat(position.getValue()).isEqualTo(0); + } + + @Test + void 두_칸_사다리_선_이동() { + //when + GreaterThanOne numberOfPerson = GreaterThanOne.from(2); + Row row = Row.from(numberOfPerson); + row.drawLine(Position.from(0)); + + //given + Position position = Position.from(0); + row.nextPosition(position); + + //then + assertThat(position.getValue()).isEqualTo(1); + + //given + position = Position.from(1); + row.nextPosition(position); + + //then + assertThat(position.getValue()).isEqualTo(0); + } + + @Test + void 세_칸_사다리_선_이동() { + //when + GreaterThanOne numberOfPerson = GreaterThanOne.from(3); + Row row = Row.from(numberOfPerson); + row.drawLine(Position.from(0)); + + //given + Position position = Position.from(0); + row.nextPosition(position); + + //then + assertThat(position.getValue()).isEqualTo(1); + + //given + position = Position.from(1); + row.nextPosition(position); + + //then + assertThat(position.getValue()).isEqualTo(0); + + //given + position = Position.from(2); + row.nextPosition(position); + + //then + assertThat(position.getValue()).isEqualTo(2); + } + + @Test + void 사다리_사람수_예외_처리() { + assertThatThrownBy(() -> Row.from(GreaterThanOne.from(1))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 사다리_최대_사람수_초과_예외() { + //when + GreaterThanOne numberOfPerson = GreaterThanOne.from(3); + Row row = Row.from(numberOfPerson); + + //given + Position position = Position.from(3); + + //then + assertThatThrownBy(() -> row.nextPosition(position)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 사다리_최소_사람수_미만_예외() { + //when + GreaterThanOne numberOfPerson = GreaterThanOne.from(3); + Row row = Row.from(numberOfPerson); + + //given + + //then + assertThatThrownBy(() -> row.nextPosition(Position.from(-1))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 사다리_그리기_위치_초과_예외() { + //when + GreaterThanOne numberOfPerson = GreaterThanOne.from(3); + Row row = Row.from(numberOfPerson); + + //given + Position position = Position.from(3); + + //then + assertThatThrownBy(() -> row.drawLine(position)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 사다리_그리기_위치_미만_예외() { + //when + GreaterThanOne numberOfPerson = GreaterThanOne.from(3); + Row row = Row.from(numberOfPerson); + + //given + + //then + assertThatThrownBy(() -> row.drawLine(Position.from(-1))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 사다리_그리기_좌측_선_중복_예외() { + //when + GreaterThanOne numberOfPerson = GreaterThanOne.from(3); + Row row = Row.from(numberOfPerson); + row.drawLine(Position.from(0)); + + //then + assertThatThrownBy(() -> row.drawLine(Position.from(1))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void 사다리_그리기_우측_선_중복_예외() { + //when + GreaterThanOne numberOfPerson = GreaterThanOne.from(3); + Row row = Row.from(numberOfPerson); + row.drawLine(Position.from(1)); + + //then + assertThatThrownBy(() -> row.drawLine(Position.from(0))) + .isInstanceOf(IllegalArgumentException.class); + + } +} \ No newline at end of file