diff --git a/build.gradle b/build.gradle index 239f9e7..6302621 100644 --- a/build.gradle +++ b/build.gradle @@ -14,6 +14,9 @@ dependencies { testImplementation platform('org.assertj:assertj-bom:3.25.1') testImplementation('org.junit.jupiter:junit-jupiter') testImplementation('org.assertj:assertj-core') + implementation 'ch.qos.logback:logback-classic:1.5.6' + implementation 'ch.qos.logback:logback-core:1.5.6' + implementation 'org.slf4j:slf4j-api:2.1.0-alpha1' } test { diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 0000000..2efec5d --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,14 @@ +import model.Ladder; +import utils.ExceptionHandler; +import view.LadderView; + +public class Application { + public static void main(String[] args) { + try { + Ladder ladder = Ladder.of(4, 4); + LadderView.printLadder(ladder); + } catch (Exception e) { + ExceptionHandler.handleException(e); + } + } +} diff --git a/src/main/java/model/Ladder.java b/src/main/java/model/Ladder.java new file mode 100644 index 0000000..e6fee46 --- /dev/null +++ b/src/main/java/model/Ladder.java @@ -0,0 +1,17 @@ +package model; + +public class Ladder { + private final Lines lines; + + public Ladder(Lines lines) { + this.lines = lines; + } + + public static Ladder of(int width, int height) { + return new Ladder(LadderGenerator.generate(width, height)); + } + + public Lines getLines() { + return lines; + } +} diff --git a/src/main/java/model/LadderGenerator.java b/src/main/java/model/LadderGenerator.java new file mode 100644 index 0000000..7fe2252 --- /dev/null +++ b/src/main/java/model/LadderGenerator.java @@ -0,0 +1,7 @@ +package model; + +public class LadderGenerator { + public static Lines generate(int width, int height) { + return LineGenerator.generate(width, height); + } +} diff --git a/src/main/java/model/LadderValidator.java b/src/main/java/model/LadderValidator.java new file mode 100644 index 0000000..8211983 --- /dev/null +++ b/src/main/java/model/LadderValidator.java @@ -0,0 +1,32 @@ +package model; + +import java.util.*; +import java.util.stream.*; + +public class LadderValidator { + private static final Random RANDOM = new Random(); + + public static void validate(List lines, int width) { + IntStream.rangeClosed(0, width) + .forEach(i -> validateColumn(lines, i, width)); + } + + private static void validateColumn(List lines, int col, int width) { + boolean emptyColumn = lines.stream().noneMatch(line -> hasBridgeAt(line, col, width)); + if (emptyColumn) { + connect(lines.get(RANDOM.nextInt(lines.size())), col, width); + } + } + + private static boolean hasBridgeAt(Line line, int col, int width) { + if (col == 0) return line.hasBridgeAt(col); + if (col == width) return line.hasBridgeAt(col - 1); + return line.hasBridgeAt(col - 1) || line.hasBridgeAt(col); + } + + + private static void connect(Line line, int col, int width) { + if (col == width) line.setBridgeAt(col - 1); + else line.setBridgeAt(col); + } +} diff --git a/src/main/java/model/Line.java b/src/main/java/model/Line.java new file mode 100644 index 0000000..46cdef1 --- /dev/null +++ b/src/main/java/model/Line.java @@ -0,0 +1,23 @@ +package model; + +import java.util.*; + +public class Line { + private final List points; + + public Line(List points) { + this.points = points; + } + + public List getPoints() { + return points; + } + + public boolean hasBridgeAt(int index) { + return points.get(index); + } + + public void setBridgeAt(int index) { + points.set(index, true); + } +} diff --git a/src/main/java/model/LineGenerator.java b/src/main/java/model/LineGenerator.java new file mode 100644 index 0000000..e666dce --- /dev/null +++ b/src/main/java/model/LineGenerator.java @@ -0,0 +1,19 @@ +package model; + +import java.util.*; + +public class LineGenerator { + + public static Lines generate(int width, int height) { + List> reserved = ReservedPositionGenerator.generate(width - 1, height); + List lines = new ArrayList<>(); + + for (int row = 0; row < height; row++) { + Line line = SingleLineGenerator.generate(width - 1, reserved.get(row)); + lines.add(line); + } + + LadderValidator.validate(lines, width - 1); + return new Lines(lines); + } +} diff --git a/src/main/java/model/Lines.java b/src/main/java/model/Lines.java new file mode 100644 index 0000000..287fb87 --- /dev/null +++ b/src/main/java/model/Lines.java @@ -0,0 +1,15 @@ +package model; + +import java.util.List; + +public class Lines { + private final List lines; + + public Lines(List lines) { + this.lines = lines; + } + + public List getLines() { + return lines; + } +} diff --git a/src/main/java/model/ReservedPositionGenerator.java b/src/main/java/model/ReservedPositionGenerator.java new file mode 100644 index 0000000..7772094 --- /dev/null +++ b/src/main/java/model/ReservedPositionGenerator.java @@ -0,0 +1,50 @@ +package model; + +import java.util.*; +import java.util.stream.*; + +public class ReservedPositionGenerator { + private static final Random random = new Random(); + + public static List> generate(int width, int height) { + List> reserved = initializeReservedList(height); + List positions = createShuffledPositions(width); + + assignPositions(reserved, positions); + + return reserved; + } + + private static List> initializeReservedList(int height) { + return IntStream.range(0, height) + .mapToObj(i -> new HashSet()) + .collect(Collectors.toList()); + } + + private static List createShuffledPositions(int width) { + List positions = IntStream.range(0, width) + .boxed() + .collect(Collectors.toList()); + + positions.add(random.nextInt(width)); + + do { + Collections.shuffle(positions, random); + } while (isSequence(positions)); + + return positions; + } + + private static boolean isSequence(List numbers) { + return IntStream.range(0, numbers.size() - 1) + .anyMatch(i -> numbers.get(i).equals(numbers.get(i + 1))); + } + + + + private static void assignPositions(List> reserved, List positions) { + for (int i = 0; i < reserved.size(); i++) { + reserved.get(i).add(positions.get(i)); + } + } +} diff --git a/src/main/java/model/SingleLineGenerator.java b/src/main/java/model/SingleLineGenerator.java new file mode 100644 index 0000000..bbdfbe2 --- /dev/null +++ b/src/main/java/model/SingleLineGenerator.java @@ -0,0 +1,29 @@ +package model; + +import java.util.*; +import java.util.stream.*; + +public class SingleLineGenerator { + private static final Random RANDOM = new Random(); + + public static Line generate(int width, Set reserved) { + List points = new ArrayList<>(Collections.nCopies(width, false)); + + reserved.forEach(i -> points.set(i, true)); + + IntStream.range(0, width).forEach(i -> { + if (!points.get(i) && isNotOverlap(points, i)) { + points.set(i, RANDOM.nextBoolean()); + } + }); + + return new Line(points); + } + + private static boolean isNotOverlap(List points, int i) { + if (i < points.size() - 1 && points.get(i + 1)) return false; + if (i > 0 && points.get(i - 1)) return false; + + return true; + } +} diff --git a/src/main/java/utils/ExceptionHandler.java b/src/main/java/utils/ExceptionHandler.java new file mode 100644 index 0000000..7156a5e --- /dev/null +++ b/src/main/java/utils/ExceptionHandler.java @@ -0,0 +1,12 @@ +package utils; + +import org.slf4j.*; + +public class ExceptionHandler { + private static final Logger logger = LoggerFactory.getLogger(ExceptionHandler.class); + + public static void handleException(Exception e) { + System.err.println("시스템 오류 : " + e.getMessage()); + logger.error("시스템 오류 : ", e); + } +} diff --git a/src/main/java/view/LadderView.java b/src/main/java/view/LadderView.java new file mode 100644 index 0000000..08d760a --- /dev/null +++ b/src/main/java/view/LadderView.java @@ -0,0 +1,31 @@ +package view; + +import model.*; + +public class LadderView { + private static final String BRIDGE = "-----|"; + private static final String SPACE = " |"; + private static final String BAR = "|"; + + public static void printLadder(Ladder ladder) { + printLines(ladder.getLines()); + } + + private static void printLines(Lines lines) { + lines.getLines().forEach(LadderView::printLine); + } + + private static void printLine(Line line) { + System.out.print(BAR); + line.getPoints().forEach(LadderView::printPoint); + System.out.println(); + } + + private static void printPoint(Boolean point) { + if (point) { + System.out.print(BRIDGE); + return; + } + System.out.print(SPACE); + } +}