Skip to content
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

2단계 - 사다리 (생성) #2319

Merged
merged 17 commits into from
Apr 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,29 @@
- [x] Optional
- [x] Optional을 활용해 조건에 따른 반환
- [x] Optional에서 값을 반환
- [x] Optional에서 exception 처리
- [x] Optional에서 exception 처리
## 2단계
- [x] LadderLine 구현
- [x] LadderLineGenerator 구현
- [x] LadderGame, Ladder 구현
- [x] Players 구현
- [x] Views 구현
- [x] LadderGameApplication 구현
- [x] 리팩토링
- [x] Player 구현
- [x] Views 리팩토링
- [x] LottoGameApplication 에서 메소드 분리
- [x] ResultView 기능 단위로 메소드 분리
- [x] Height 구현
- [x] 피드백
- [x] Player
- [x] 상수 추가
- [x] isEmpty / isBlank 선택
- [x] LadderLineGenerator
- [x] 상수 추가
- [x] 테스트 편의 위해 인터페이스 도입
- [x] Ladder
- [x] LadderLine 리스트 반환 메소드 추가
- [x] LadderLine
- [x] is -> has
- [x] 조건문 통합
Empty file removed docs/issues_step1.md
Empty file.
21 changes: 21 additions & 0 deletions docs/issues_step2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 2단계 - 사다리 (생성)
## 사다리 구현
### Ladder
- 사다리 객체는 사다리의 높이와 폭을 가지고 있어야 한다.
### LadderLine
- 사다리 줄은 List<Boolean> 필드를 가진다
- 사다리의 폭은 사람 수보다 1 적다
- List<Boolean> 내부의 값은 사다리 줄이 있는지 없는지를 나타낸다.
### LadderLineGenerator
- 내부에서 상태를 가지고 있을 필요가 없어서 static 메소드 제공하는 형태로 구현
- 무작위 값을 사용하는 메소드가 존재해 테스트가 어려우므로 인터페이스 의존
- LadderLine에서 List<Boolean>을 검증하는 메소드 필요
### LadderGame
- 참여 인원, 사다리 높이를 입력받아 사다리 객체를 생성한다.
### Player
- isEmpty vs. isBlank
- isEmpty는 문자열의 길이가 0일 경우 true
- isBlank는 문자열이 비어있거나 공백으로만 이루어져 있을 경우 true
## IntelliJ
- 신규 파일 생성 시 Github 자동 추가
- 버전 관리 -> 확인
45 changes: 45 additions & 0 deletions docs/todo_step2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# 2단계 - 사다리(생성)
## 기능 요구사항
- 사다리 게임에 참여하는 사람에 이름을 최대5글자까지 부여할 수 있다. 사다리를 출력할 때 사람 이름도 같이 출력한다.
- 사람 이름은 쉼표(,)를 기준으로 구분한다.
- 사람 이름을 5자 기준으로 출력하기 때문에 사다리 폭도 넓어져야 한다.
- 사다리 타기가 정상적으로 동작하려면 라인이 겹치지 않도록 해야 한다.
- |-----|-----| 모양과 같이 가로 라인이 겹치는 경우 어느 방향으로 이동할지 결정할 수 없다.
## 프로그래밍 요구사항
- 자바 8의 스트림과 람다를 적용해 프로그래밍한다.
- 규칙 6: 모든 엔티티를 작게 유지한다.
### 실행 결과
- 위 요구사항에 따라 4명의 사람을 위한 5개 높이 사다리를 만들 경우, 프로그램을 실행한 결과는 다음과 같다.
```text
참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)
pobi,honux,crong,jk

최대 사다리 높이는 몇 개인가요?
5

실행결과

pobi honux crong jk
|-----| |-----|
| |-----| |
|-----| | |
| |-----| |
|-----| |-----|

```
## 힌트
- 2차원 배열을 ArrayList, Generic을 적용해 구현하면 `ArrayList<ArrayList<Boolean>>`와 같이 이해하기 어려운 코드가 추가된다.
- 사다리 게임에서 한 라인의 좌표 값을 가지는 객체를 추가해 구현해 본다.
```java
public class Line {
private List<Boolean> points = new ArrayList<>();

public Line (int countOfPerson) {
// 라인의 좌표 값에 선이 있는지 유무를 판단하는 로직 추가
}

[...]
}

```
- 위와 같이 Line 객체를 추가하면 `ArrayList<ArrayList<Boolean>>` 코드를 `ArrayList<Line>`과 같이 구현하는 것이 가능해 진다.
16 changes: 16 additions & 0 deletions src/main/java/ladder/Height.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ladder;

public class Height {
private final int value;

public Height(int value) {
if (value <= 0) {
throw new IllegalArgumentException("Height must be greater than 0.");
}
this.value = value;
}

public int value() {
return value;
}
}
16 changes: 16 additions & 0 deletions src/main/java/ladder/LadderGameApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ladder;

import ladder.domain.LadderGame;
import ladder.domain.Players;
import ladder.views.InputView;
import ladder.views.ResultView;

public class LadderGameApplication {
public static void main(String[] args) {
Players players = InputView.createPlayersWithQuery("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)");
Height height = InputView.createIntegerWithQuery("최대 사다리 높이는 몇 개인가요?");
LadderGame ladderGame = new LadderGame(players, height);

ResultView.printResult(ladderGame);
}
}
36 changes: 36 additions & 0 deletions src/main/java/ladder/domain/Ladder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ladder.domain;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Ladder {
private final List<LadderLine> ladderLines;

public Ladder(int height, int width, LadderLineGenerator ladderLineGenerator) {
if (height < 1 || width < 1) {
throw new IllegalArgumentException("Height and width must be at least 1.");
}

this.ladderLines = IntStream.range(0, height)
.mapToObj(i -> new LadderLine(ladderLineGenerator.generateLadderLine(width)))
.collect(Collectors.toList());
}

public int height() {
return ladderLines.size();
}

public int width() {
if (ladderLines.isEmpty()) {
return 0;
}
return ladderLines.get(0).size();
}

public List<LadderLine> getLadderLinesCopy() {
return ladderLines.stream()
.map(LadderLine::copy)
.collect(Collectors.toList());
}
}
10 changes: 10 additions & 0 deletions src/main/java/ladder/domain/LadderConstants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ladder.domain;

public class LadderConstants {

private LadderConstants() {}

public static final int MAX_NAME_LENGTH = 5;
public static final double RANDOM_TRUE_THRESHOLD = 0.5;

}
22 changes: 22 additions & 0 deletions src/main/java/ladder/domain/LadderGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ladder.domain;

import ladder.Height;
import ladder.domain.ladderlinegenerator.RandomLadderLineGenerator;

public class LadderGame {
private final Players players;
private final Height height;

public LadderGame(Players players, Height height) {
this.players = players;
this.height = height;
}

public Ladder createLadder() {
return new Ladder(height.value(), players.count() - 1, new RandomLadderLineGenerator());
}

public Players players() {
return players;
}
}
59 changes: 59 additions & 0 deletions src/main/java/ladder/domain/LadderLine.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package ladder.domain;

import java.util.List;
import java.util.stream.IntStream;

public class LadderLine {
private final List<Boolean> points;

public LadderLine(List<Boolean> points) {
if (!isValid(points)) {
throw new IllegalArgumentException("Invalid points");
}
this.points = points;
}

public int move(int startPosition) {
if (startPosition < 0 || startPosition > points.size()) {
throw new IllegalArgumentException("Invalid start position");
}

if (isRightConnected(startPosition)) return startPosition + 1;
if (isLeftConnected(startPosition)) return startPosition - 1;

return startPosition;
}

public int size() {
return points.size();
}

public boolean getPoint(int index) {
if (index < 0 || index >= points.size()) {
throw new IllegalArgumentException("Invalid index");
}
return points.get(index);
}

private boolean isValid(List<Boolean> points) {
return !isEmpty(points) && IntStream.range(0, points.size() - 1)
.noneMatch(i -> points.get(i) && points.get(i + 1));
}

private boolean isEmpty(List<Boolean> points) {
return points == null || points.isEmpty();
}

private boolean isLeftConnected(int startPosition) {
return startPosition > 0 && points.get(startPosition - 1);
}

private boolean isRightConnected(int startPosition) {
return startPosition < points.size() && points.get(startPosition);
}

public LadderLine copy() {
List<Boolean> copiedPoints = List.copyOf(points);
return new LadderLine(copiedPoints);
}
}
7 changes: 7 additions & 0 deletions src/main/java/ladder/domain/LadderLineGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ladder.domain;

import java.util.List;

public interface LadderLineGenerator {
List<Boolean> generateLadderLine(int width);
}
31 changes: 31 additions & 0 deletions src/main/java/ladder/domain/Player.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package ladder.domain;

public class Player {
private final String name;

public Player(String name) {
validateName(name);
this.name = name;
}

private void validateName(String name) {
checkNullOrEmpty(name);
checkNameLength(name);
}

private void checkNameLength(String name) {
if (name.length() > LadderConstants.MAX_NAME_LENGTH) {
throw new IllegalArgumentException("Player name cannot be longer than 5 characters");
}
}

private void checkNullOrEmpty(String name) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("Player name cannot be null or empty");
}
}

public String name() {
return name;
}
}
26 changes: 26 additions & 0 deletions src/main/java/ladder/domain/Players.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ladder.domain;

import java.util.List;
import java.util.stream.Collectors;

public class Players {
private final List<Player> players;

public Players(List<String> players) {
if (players == null || players.isEmpty()) {
throw new IllegalArgumentException("Players list cannot be null or empty");
}

this.players = players.stream()
.map(Player::new)
.collect(Collectors.toList());
}

public int count() {
return players.size();
}

public Player getPlayerAtIndex(int i) {
return players.get(i);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package ladder.domain.ladderlinegenerator;

import ladder.domain.LadderLineGenerator;

import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static ladder.domain.LadderConstants.RANDOM_TRUE_THRESHOLD;

public class RandomLadderLineGenerator implements LadderLineGenerator {

public RandomLadderLineGenerator() {}

public List<Boolean> generateLadderLine(int size) {
if (size < 1) {
throw new IllegalArgumentException("사다리의 길이는 1 이상이어야 합니다.");
}

AtomicBoolean previousValue = new AtomicBoolean(false);

return IntStream.range(0, size)
.mapToObj(i -> determineNextValue(previousValue))
.collect(Collectors.toList());
}

private boolean determineNextValue(AtomicBoolean previousValue) {
boolean currentValue = Math.random() > RANDOM_TRUE_THRESHOLD;
if (previousValue.get() && currentValue) {
currentValue = false;
}
previousValue.set(currentValue);
return currentValue;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ladder.domain.ladderlinegenerator;

import ladder.domain.LadderLineGenerator;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class TrueLadderLineGenerator implements LadderLineGenerator {
@Override
public List<Boolean> generateLadderLine(int width) {
return IntStream.range(0, width)
.mapToObj(i -> true)
.collect(Collectors.toList());
}
}
Loading