-
Notifications
You must be signed in to change notification settings - Fork 66
[그리디] 김민욱 사다리 미션 1~5 단계 제출합니다 #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: hapdaypy
Are you sure you want to change the base?
Changes from 28 commits
8c68e8f
c64a687
8bec14e
9ab451e
75be806
5c216cb
4416863
96cdcde
05c8532
3813be0
5f81de0
8242c8c
7e90e30
cca1c3f
a12701e
4444739
c2f8034
c4a8b02
5644c84
160c1bd
821e9d2
ea9a9a0
3007e43
9bd9cc1
035a320
a38db34
b3cf1a4
d03a637
cec2e4f
b821da1
ad72977
ea59aca
895b46b
3b5ff8d
d9c9a92
d91171f
0eb57b8
aebc5ed
c7eb9f2
f4c7986
bea7a83
f6bc97d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import controller.LadderController; | ||
| import view.InputView; | ||
| import view.OutputView; | ||
|
|
||
| public class Application { | ||
| public static void main(String[] args) { | ||
| InputView inputView = new InputView(); | ||
| OutputView outputView = new OutputView(); | ||
| LadderController controller = new LadderController(inputView, outputView); | ||
| controller.run(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| package controller; | ||
|
|
||
| import domain.*; | ||
| import view.InputView; | ||
| import view.OutputView; | ||
|
|
||
| public class LadderController { | ||
| private final InputView inputView; | ||
| private final OutputView outputView; | ||
|
|
||
| public LadderController(InputView inputView, OutputView outputView) { | ||
| this.inputView = inputView; | ||
| this.outputView = outputView; | ||
| } | ||
|
|
||
| public void run() { | ||
| Players players = inputView.readPlayers(); | ||
| Rewards rewards = inputRewards(players); | ||
| LadderHeight height = new LadderHeight(inputView.readHeight()); | ||
|
|
||
| Ladder ladder = Ladder.generate(new LadderWidth(players.size()), height, new RandomBooleanGenerator()); | ||
| outputView.printLadderBoard(players, ladder, rewards); | ||
|
|
||
| GameResult gameResult = createGameResult(players, rewards, ladder); | ||
| printTargetResults(gameResult); | ||
| } | ||
|
|
||
| private Rewards inputRewards(Players players) { | ||
| Rewards rewards = inputView.readRewards(); | ||
| players.validateMatch(rewards); | ||
| return rewards; | ||
| } | ||
|
|
||
| private GameResult createGameResult(Players players, Rewards rewards, Ladder ladder) { | ||
| LadderResult ladderResult = ladder.play(new LadderWidth(players.size())); | ||
| return GameResult.of(players, rewards, ladderResult); | ||
| } | ||
|
|
||
| private void printTargetResults(GameResult gameResult) { | ||
| while (true) { | ||
| String target = inputView.readTargetPerson(); | ||
| if (processTarget(target, gameResult)) { | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private boolean processTarget(String target, GameResult gameResult) { | ||
| if ("all".equals(target)) { | ||
| outputView.printAllResults(gameResult); | ||
| return true; | ||
| } | ||
| outputView.printSingleResult(gameResult.findByName(target)); | ||
| return false; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package domain; | ||
|
|
||
|
|
||
| public interface BooleanGenerator { | ||
| boolean generate(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package domain; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| public class GameResult { | ||
| private final List<Player> players; | ||
|
|
||
| public GameResult(List<Player> players) { | ||
| this.players = players; | ||
| } | ||
|
|
||
| public static GameResult of(Players players, Rewards rewards, LadderResult ladderResult) { | ||
| List<Player> playerList = new ArrayList<>(); | ||
| for (int i = 0; i < players.size(); i++) { | ||
| playerList.add(createPlayer(i, players, rewards, ladderResult)); | ||
| } | ||
| return new GameResult(playerList); | ||
| } | ||
|
|
||
| private static Player createPlayer(int index, Players players, Rewards rewards, LadderResult result) { | ||
| Position start = new Position(index); | ||
| Position end = result.getEndPosition(start); | ||
| return new Player(players.getName(index), rewards.getReward(end.getValue())); | ||
| } | ||
|
|
||
| public Player findByName(String targetName) { | ||
| return players.stream() | ||
| .filter(player -> player.hasName(targetName)) | ||
| .findFirst() | ||
| .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 사람입니다.")); | ||
| } | ||
|
|
||
| public List<Player> getAll() { | ||
| return Collections.unmodifiableList(players); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| package domain; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.LinkedHashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.stream.IntStream; | ||
|
|
||
| public class Ladder { | ||
| private final List<Line> lines; | ||
|
|
||
| public Ladder(List<Line> lines) { | ||
| this.lines = lines; | ||
| } | ||
|
|
||
| public static Ladder generate(LadderWidth width, LadderHeight height, BooleanGenerator generator) { | ||
| Ladder ladder = createLadder(width, height, generator); | ||
| while (ladder.hasEmptyInterval(width)) { | ||
| ladder = createLadder(width, height, generator); | ||
| } | ||
| return ladder; | ||
| } | ||
|
|
||
| private static Ladder createLadder(LadderWidth width, LadderHeight height, BooleanGenerator generator) { | ||
| List<Line> lines = new ArrayList<>(); | ||
| Line currentLine = Line.generateFirst(width, generator); | ||
| lines.add(currentLine); | ||
| return new Ladder(addRemainingLines(lines, width, height, generator, currentLine)); | ||
| } | ||
|
|
||
| private static List<Line> addRemainingLines(List<Line> lines, LadderWidth width, LadderHeight height, BooleanGenerator generator, Line firstLine) { | ||
| Line currentLine = firstLine; | ||
| for (int i = 1; i < height.getValue(); i++) { | ||
| currentLine = Line.generateNext(width, generator, currentLine); | ||
| lines.add(currentLine); | ||
| } | ||
| return lines; | ||
| } | ||
|
|
||
| private boolean hasEmptyInterval(LadderWidth width) { | ||
| return IntStream.range(0, width.getIntervalCount()) | ||
| .anyMatch(this::isEmptyInterval); | ||
| } | ||
|
|
||
| private boolean isEmptyInterval(int index) { | ||
| return lines.stream().noneMatch(line -> line.isConnectedAt(index)); | ||
| } | ||
|
|
||
| public LadderResult play(LadderWidth width) { | ||
| Map<Position, Position> results = new LinkedHashMap<>(); | ||
| for (int i = 0; i < width.getValue(); i++) { | ||
| Position startPosition = new Position(i); | ||
| results.put(startPosition, playOne(startPosition)); | ||
| } | ||
| return new LadderResult(results); | ||
| } | ||
|
|
||
| private Position playOne(Position startPosition) { | ||
| Position current = startPosition; | ||
| for (Line line : lines) { | ||
| current = line.move(current); | ||
| } | ||
| return current; | ||
| } | ||
|
|
||
| public List<Line> getLines() { | ||
| return Collections.unmodifiableList(lines); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package domain; | ||
|
|
||
| public class LadderHeight { | ||
| private static final int MINIMUM_HEIGHT = 1; | ||
| private static final String ERROR_MESSAGE = "사다리 높이는 1 이상이어야 합니다."; | ||
|
|
||
| private final int value; | ||
|
|
||
| public LadderHeight(int value) { | ||
| validate(value); | ||
| this.value = value; | ||
| } | ||
|
|
||
| private void validate(int value) { | ||
| if (value < MINIMUM_HEIGHT) { | ||
| throw new IllegalArgumentException(ERROR_MESSAGE); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| public int getValue() { | ||
| return value; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package domain; | ||
| import java.util.Map; | ||
|
|
||
|
|
||
| public class LadderResult { | ||
| private final Map<Position, Position> results; | ||
|
|
||
| public LadderResult(Map<Position, Position> results) { | ||
| this.results = results; | ||
| } | ||
|
|
||
| public Position getEndPosition(Position start) { | ||
| return results.get(start); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package domain; | ||
|
|
||
| public class LadderWidth { | ||
| private static final int MINIMUM_WIDTH = 2; | ||
| private static final String ERROR_MESSAGE = "사다리 폭은 2 이상이어야 합니다."; | ||
|
|
||
| private final int value; | ||
|
|
||
| public LadderWidth(int value) { | ||
| validate(value); | ||
| this.value = value; | ||
| } | ||
|
|
||
| private void validate(int value) { | ||
| if (value < MINIMUM_WIDTH) { | ||
| throw new IllegalArgumentException(ERROR_MESSAGE); | ||
| } | ||
| } | ||
|
|
||
| public int getValue() { | ||
| return value; | ||
| } | ||
|
|
||
| public int getIntervalCount() { | ||
| return value - 1; | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| package domain; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| public class Line { | ||
| private final List<Boolean> points; | ||
|
|
||
| public Line(List<Boolean> points) { | ||
| this.points = points; | ||
| } | ||
|
|
||
| public static Line generateFirst(LadderWidth width, BooleanGenerator generator) { | ||
| List<Boolean> points = new ArrayList<>(); | ||
| boolean previous = false; | ||
| for (int i = 0; i < width.getIntervalCount(); i++) { | ||
| previous = addPoint(points, previous, false, generator); | ||
| } | ||
| return new Line(points); | ||
| } | ||
|
|
||
| public static Line generateNext(LadderWidth width, BooleanGenerator generator, Line previousLine) { | ||
| List<Boolean> points = new ArrayList<>(); | ||
| boolean previous = false; | ||
| for (int i = 0; i < width.getIntervalCount(); i++) { | ||
| boolean above = previousLine.isConnectedAt(i); | ||
| previous = addPoint(points, previous, above, generator); | ||
| } | ||
| return new Line(points); | ||
| } | ||
|
|
||
| private static boolean addPoint(List<Boolean> points, boolean previous, boolean above, BooleanGenerator gen) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. L14에서는 BooleanGenerator 의 변수명을 generator로 적어주셨는데, 여기서는 gen으로 축약해서 적어주셨네요. 같은 매게인자인데도 불구하고 네이밍이 다른 이유는 무엇인가요? 축약형은 지양하자가 초반 요구사항이었던 것 같아서요.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 축약형으로 써서 제출했던 것은 제 부주의였습니다... 축약형 사용을 지양하고 학습 초기부터 잘못된 습관을 바로잡아야 하는 이유는, 시간이 지나면 코드를 작성한 사람이나 읽는 사람 모두 그 의미를 파악하기 어렵기 때문입니다. 여러 개의 변수명이 전부 축약형으로 적혀 있다면 코드를 이해하는 데 불필요한 시간과 노력을 쏟아야 하므로, 학습 초기부터 이런 잘못된 습관을 반드시 바로잡아야 한다고 생각합니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
저랑 같은 생각을 가지고 계시는 군요~ 추가적으로 제가 생각하는 다른 이유는 |
||
| boolean nextPoint = determineNext(previous, above, gen); | ||
| points.add(nextPoint); | ||
| return nextPoint; | ||
| } | ||
|
|
||
| private static boolean determineNext(boolean previous, boolean above, BooleanGenerator gen) { | ||
| if (previous || above) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 변수명을 이렇게 처음 봤을 때는 이전, 위 라는 맥락뿐이어서 이해하는 데 다소 시간이 걸렸던 것 같아요. 좀 더 의미 있는 변수명을 사용해보는 건 어떨까요?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네! 맞습니다! 코드는 여러 개의 변수들이 모여서 하나의 큰 흐름을 이룬다고 생각합니다. 그 흐름을 이해하기 위해서는 변수명의 설정이 무엇보다 중요합니다. 만약 변수명이 단순한 영어 단어 하나라면, 그 변수가 의도하는 바를 명확하게 전달하기 어려울 것이라는 점을 깨달았습니다. 앞으로는 단순한 영어 단어의 나열이 아니라, 다른 사람이 코드를 읽었을 때 그 의미를 한 번에 납득할 수 있는 명확한 변수명을 짓도록 의식적으로 노력하겠습니다! 리뷰어님의 소중한 시간을 뺏어가서 죄송합니다... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제가 남긴 코멘트를 다시 읽어보니, 사실은 민욱님이 어떤 기준으로 네이밍을 하시는지 궁금했던 건데, 정작 그렇게 질문을 드리지는 못했더라고요. 그래도 스터디 자리에서나마 이에 대한 생각을 들어볼 수 있어서 좋았습니다! 민기님 말씀처럼, |
||
| return false; | ||
| } | ||
| return gen.generate(); | ||
| } | ||
|
|
||
| public boolean isConnectedAt(int index) { | ||
| return points.get(index); | ||
| } | ||
|
|
||
| public Position move(Position position) { | ||
| int currentIndex = position.getValue(); | ||
| if (canMoveLeft(currentIndex)) { | ||
| return position.moveLeft(); | ||
| } | ||
| if (canMoveRight(currentIndex)) { | ||
| return position.moveRight(); | ||
| } | ||
| return position; | ||
| } | ||
|
|
||
| private boolean canMoveLeft(int currentIndex) { | ||
| if (currentIndex <= 0) { | ||
| return false; | ||
| } | ||
| return points.get(currentIndex - 1); | ||
| } | ||
|
|
||
| private boolean canMoveRight(int currentIndex) { | ||
| if (currentIndex >= points.size()) { | ||
| return false; | ||
| } | ||
| return points.get(currentIndex); | ||
| } | ||
|
|
||
| public List<Boolean> getPoints() { | ||
| return Collections.unmodifiableList(points); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package domain; | ||
|
|
||
|
|
||
| public class Player { | ||
| private final PlayerName name; | ||
| private final Reward reward; | ||
|
|
||
| public Player(PlayerName name, Reward reward) { | ||
| this.name = name; | ||
| this.reward = reward; | ||
| } | ||
|
|
||
| public boolean hasName(String targetName) { | ||
| return this.name.getValue().equals(targetName); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. getter로 객체 내부의 값을 꺼내서 Player가 직접 비교하고 있는데요,
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 처음 저는 Player가 PlayerName의 값을 꺼내와서 자바에서 제공하는 equals API로 비교해도 문제가 없다고 생각했습니다. 그동안 객체 내부의 값을 반환하는 것을 지양해야 하는 주된 이유가 '외부에서 값을 수정할 위험' 때문이라고 생각했는데, 안전한 자바 API와 불변 객체를 사용한다면 그 위험을 피할 수 있다고 판단했기 때문입니다. 하지만 핵심은 값의 변질 여부가 아니라, 객체의 책임과 자율성에 관한 문제였습니다! 리뷰어님의 말씀대로 Player라는 외부 객체가 PlayerName 내부의 값을 직접 꺼내어 비교하는 것은 객체의 자율성을 지키지 못한 방식이라고 생각합니다. 이를 리팩토링한다면, Player는 비교를 요청하기만 하고 직접적인 비교 로직은 값을 가지고 있는 PlayerName 스스로가 처리하는 방식으로 리팩토링을 진행할 수 있겠습니다 ! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 좋습니다~~ 맞아요 제가 이야기 하고 싶었던 부분은 객체의 책임에 대한 이야기였습니다.
이렇게도 풀 수 있겠네요~ 그럼 제가 처음에 코멘트를 남긴 방향성부터 말씀드려볼게요!객체의 자율성 이야기가 조금 추상적으로 느껴질 수 있을 것 같아서, 좀 더 현실적인 이유를 들어볼게요. 저는 PlayerName이라는 값 객체를 만든 이유 자체가, "이름"이라는 개념에 단순한 문자열 이상의 규칙이 붙을 수 있기 때문이라고 생각해요. 예를 들면 이런 상황들이요.
그래서 지금은 비교가 equals로 끝나지만, 나중에 "공백을 무시하고 비교해줘"라는 요구사항이 생겼다고 가정을 해볼게요! this.name.getValue().equals(targetName)이 코드를 쓰는 모든 곳에서 this.name.equals(new PlayerName(targetName))
// 또는
this.name.matches(targetName)규칙이 바뀌더라도 PlayerName 안의 메서드 하나만 고치면 될 거에요! 가정한 시나리오가 그렇게 현실적이지 않을 수도 있겠지만, 결국 PlayerName이라는 값 객체를 만든 이유는 "이름은 그냥 String이 아니라 자기만의 규칙을 가진 개념이다"라고 선언하고 싶은 거라고 생각해요. 근데 비교할 때마다 getValue()로 String을 꺼내버리면, 객체에 데이터만 있고 행동은 없는 상태가 되는 것 같아요. [추가 리펙토링 방향]이미 민욱님이 충분히 같은 방향성으로 이해해주셨다고 생각해요! new PlayerName(targetName)라는 것을 확인했어요! 지금 확인하고 싶은게 이름이 같은 지 아닌 지 단순 boolean 값일 건데, 만일 생성자에 있는 검증 로직을 돌 수 도 있겠다는 생각이 들더라고요. 상황을 예로 들어보면 언어 정보도 필요하다고 가정을 해볼게요! 그러면 이름 문자열이 같은지만 묻고 싶었던 건데, locale 정보까지 신경 써야 하는 상황이 와요. 비교 행위가 PlayerName의 내부 구조에 종속되어 버리는 거죠. 반면 // PlayerName 내부
public boolean matches(String input) {
return this.value.equals(input); // 내부 사정은 PlayerName이 알게끔
}필드가 늘어나도 외부 코드는 영향을 받지 않을 거라는 생각이 들었습니다!! |
||
| } | ||
|
|
||
| public PlayerName getName() { | ||
| return name; | ||
| } | ||
|
|
||
| public Reward getReward() { | ||
| return reward; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package domain; | ||
|
|
||
| public class PlayerName { | ||
| private static final int MAX_LENGTH = 5; | ||
| private final String value; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Position에서는 equals / hashcode가 잘 구현되어 있음을 확인했어요! 다만 이 클래스, PlayerName에는 equals / hashcode가 구현되어 있지 않네요~ 둘의 설계 방식이 다른데 왜 그렇게 다르게 구현했는 지 설명해주실 수 있나요? 만일 단순 빼먹은 거라면 equals / hashcode가 없는 PlayerName에서 어떠한 상황이 초래할 수 있기 때문에 구현해야 하는 걸까요?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제가 해당 코드 작성을 누락했습니다... PlayerName 에서 equals / hashcode 를 구현해야하는 이유는 다음과 같습니다!
동일성 : 자바에서 new PlayerName("이름")를 두 번 실행하면 메모리상에 완전히 독립된 2개의 객체가 생성됩니다. 자바의 기본 equals()는 이 메모리 주소를 비교하므로 둘을 다르다고 판별합니다. => 같은 이름을 가진 서로 다른 사람 : 동명이인 동등성 : 사다리 게임 도메인에서는 이름표에 적힌 글자(value)가 같다면 같은 사람으로 취급해야 합니다. 메모리 주소가 다르더라도 내부 값이 같으면 true를 반환하도록 논리적 동등성을 부여하는 작업이 equals() 재정의입니다. => 한 사람
equals만 재정의하고 hashCode를 재정의하지 않으면, HashSet이나 HashMap 같은 해시 기반 자료구조를 사용할 때 중복 검증이나 데이터 검색을 실패하게 됩니다. 메모리 주소 기반의 엉뚱한 해시코드를 반환하기 때문입니다. 그렇다면 equals / hashcode 를 사용하지 않았다면 생길 수 있는 문제점은 다음과 같습니다!
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. equals / hashcode 구현 이유의 두가지를 정확하게 짚어주셨네요
제가 생각하는 방향성도 이와 동일합니다~
그래서 민욱님이 어떤 시나리오를 염두에 두셨는지에 따라, 이게 코드 작성의 누락이 아닐 수도 있어요. 제가 코멘트를 단 이유는, 구현하지 않은 설계에 의도가 있었던 것인지 아닌지가 궁금해서였어요. equals / hashCode 구현 여부를 스스로의 기준에 따라 고민하고 설계할 수 있는지를 고민해볼 수 있는 미션이라 생각했기 때문이에요~ 또한 이 부분은 스터디 시간에도 추가로 질문해주신 덕분에 한 번 더 질답이 오갈 수 있었고, 저도 민욱님이 어디서 헷갈리시는지 알 수 있어서 좋았어요 ㅋㅎㅋㅎ 저도 똑같이 헷갈렸던 지점이라 참 반가웠습니다.
또 어떤 부분이 있을랑가요~~ |
||
|
|
||
| public PlayerName(String value) { | ||
| if (value.length() > MAX_LENGTH) { | ||
| throw new IllegalArgumentException("이름은 5자를 초과할 수 없습니다."); | ||
| } | ||
| this.value = value; | ||
| } | ||
|
|
||
| public String getValue() { | ||
| return value; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
above = true이면 다음 point가 생기지 않도록 하는 규칙을 넣어주신 것 같아요~
따로 요구사항에는 존재하지 않았던 것 같아서 민욱님이 생각하기에 이 규칙이 필요하다고 판단한 이유가 단순히 궁금했어요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
극단적인 예시로
|[][][]|-----|[][][][]|
|-----|[][][]|[][][][]|
|-----|[][][]|------|
|-----|[][][]|[][][][]|
|-----|[][][]|[][][][]|
([]는 공백을 의미합니다)
이런 형태의 사다리는 문제가 있는 사다리의 모양이지 않을까? 생각했습니다.
문제라고 생각할 수 있었던 이유는 네이버 사다리 게임을 참고한 결과 같은 위치에 연속으로 가로줄이 배치되는 형태가 발견되지 않음을 확인했기 때문입니다!
따라서 이와같은 이유로 사다리 게임의 규칙을 추가했습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
명시된 요구사항뿐 아니라, 도메인 특성을 직접 파악해서 규칙까지 추가해 주신거군요!
도메인에 대한 이해도가 잘 드러나는 부분이라 좋네요 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
감사합니다 !!