Skip to content
68 changes: 52 additions & 16 deletions src/main/java/nextstep/ladder/LadderGame.java
Original file line number Diff line number Diff line change
@@ -1,53 +1,89 @@
package nextstep.ladder;

import nextstep.ladder.domain.Ladder;
import nextstep.ladder.domain.Line;
import nextstep.ladder.domain.Player;
import nextstep.ladder.domain.Result;
import nextstep.ladder.util.RandomUtil;
import nextstep.ladder.view.InputView;
import nextstep.ladder.view.ResultView;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.*;
import java.util.stream.IntStream;

public class LadderGame {

private static final String NAME_REX_PATTERN = ",";

private final List<Player> players = new ArrayList<>();
private static final String ALL_PLAYER = "all";

private Ladder ladder;

private Result result;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ladder, result를 필드로 관리할 필요가 있을까요?
run 메서드의 지역 변수로 관리해도 괜찮지 않을까요?


public static void main(String[] args) {
LadderGame game = new LadderGame();
game.run();
}

public void run() {

inputPlayers();
List<Player> players = new ArrayList<>();

saveLadder(new Ladder(inputLadderHeight(), this.players.size()));
inputPlayers(players, InputView.inputPlayers());

ResultView.printResult(this.players, ladder.getLines());
result = new Result(InputView.inputResult());

}
saveLadder(new Ladder(InputView.inputLadderHeight()));
addLadderLines(ladder.getHeight(), players.size());

ResultView.printLadderResult(players, ladder.getLines(), result);

String inputPlayer = InputView.inputPlayer();
printPlayerResult(inputPlayer, players);

private void inputPlayers() {
addPlayers(InputView.inputPlayers());
}

private void addPlayers(String players) {
Arrays.stream(players.split(NAME_REX_PATTERN))
.map(Player::new)
.forEach(this.players::add);
private void inputPlayers(List<Player> players, String inputPlayers) {
addPlayers(players, inputPlayers);
}

private int inputLadderHeight() {
return InputView.inputLadderHeight();
private void addPlayers(List<Player> players, String inputPlayers) {
Arrays.stream(inputPlayers.split(NAME_REX_PATTERN))
.map(Player::new)
.forEach(players::add);
}

private void saveLadder(Ladder ladder) {
this.ladder = ladder;
}

private void addLadderLines(int height, int width) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line을 만드는 것을 외부에서 만들어서 Ladder에 저장해주는데, Ladder가 높이를 알고 있으니 Ladder 내부에서 필요한만큼 Line을 만들어보면 어떨까요?

IntStream.range(0, height)
.mapToObj(i -> new Line(() -> RandomUtil.generatorPoints(width - 1)))
.forEach(this::addLine);
}

private void addLine(Line line) {
this.ladder.addLine(line);
}

private void printPlayerResult(String inputPlayer, List<Player> players) {

ResultView.printResultText();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용자의 결과를 출력하기 위해서 printPlayerResultWithName, printPlayerResult를 호출하는데 이 메서드 안에서 "실행 결과"도 출력하도록 하면 어떨까요?
"실행 결과"를 출력하기 위해 printResultText를 호출한 후 위의 메서드를 호출해야 한다는 것은 ResultView의 상세 구현을 LadderGame이 알고 순서에 맞춰 메서드를 호출해야 한다는 것으로 보입니다.


if (ALL_PLAYER.equals(inputPlayer)) {
IntStream.range(0, players.size()).forEach(index -> printPlayerResultWithName(index, players.get(index)));
return;
}

int point = players.indexOf(new Player(inputPlayer));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all을 입력하지 않은 경우에는 추가로 결과를 보고 싶은 사람을 입력할 수 있는 것으로 보입니다.
실행 결과를 참고하시어 수정해보시면 좋겠습니다.

https://edu.nextstep.camp/s/wLaV8qhA/ls/oHYSWjj7

ResultView.printPlayerResult(result.getValue(players.get(point).getPlayerResultIndex(point, ladder)));

}

private void printPlayerResultWithName(int index, Player player) {
ResultView.printPlayerResultWithName(player.getName(), result.getValue(player.getPlayerResultIndex(index, ladder)));
}

}
32 changes: 24 additions & 8 deletions src/main/java/nextstep/ladder/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
LadderGame
플레이어 추가
사다이 추가
라인 추가
# 3단계 - 사다리(게임 실행)

## 기능 요구사항
- 사다리 실행 결과를 출력해야 한다.
- 개인별 이름을 입력하면 개인별 결과를 출력하고, "all"을 입력하면 전체 참여자의 실행 결과를 출력한다.

## 프로그래밍 요구사항
- 자바 8의 스트림과 람다를 적용해 프로그래밍한다.
- 규칙 6: 모든 엔티티를 작게 유지한다.
- 규칙 7: 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.

---

### LadderGame
-[X] 플레이어 추가
-[X] 사다리 저장
-[X] 플레이어 결과 구하기

InputView
참여할 사람 이름 입력
최대 사다리 높이 입력
-[X] 참여할 사람 이름 입력
-[X] 최대 사다리 높이 입력
-[X] 실행 결과 입력
-[X] 결과를 보고 싶은 사람 입력

ResultView
플레이어 이름 출력
사다리 출력
-[X] 플레이어 이름 출력
-[X] 사다리 출력
-[X] 실행 결과 출력
27 changes: 16 additions & 11 deletions src/main/java/nextstep/ladder/domain/Ladder.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
package nextstep.ladder.domain;

import nextstep.ladder.util.RandomUtil;

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

public class Ladder {

private List<Line> lines;
private static final String HEIGHT_ERROR_MESSAGE = "높이는 0보다 커야 합니다.";

public Ladder(int height, int width) {
addLine(height, width);
private List<Line> lines = new ArrayList<>();

private int height;

public Ladder(int height) {
if (1 > height) {
throw new IllegalArgumentException(HEIGHT_ERROR_MESSAGE);
}
this.height = height;
}

public void addLine(Line line) {
lines.add(line);
}

private void addLine(int height, int width) {
List<Line> lines = new ArrayList<>();
IntStream.range(0, height)
.mapToObj(i -> new Line(width))
.forEach(lines::add);
this.lines = lines;
public int getHeight() {
return height;
}

public List<Line> getLines() {
Expand Down
24 changes: 14 additions & 10 deletions src/main/java/nextstep/ladder/domain/Line.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,29 @@

import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.IntStream;

public class Line {

private final List<Boolean> points;

public Line(int countOfPerson) {
List<Boolean> points = new ArrayList<>();
IntStream.range(0, countOfPerson - 1)
.forEach(index -> addPoint(points, index));
this.points = points;
public Line(Supplier<List<Boolean>> supplierPoints) {
this.points = supplierPoints.get();
}

private void addPoint(List<Boolean> points, int index) {
if (index == 0 || !points.get(index - 1)) {
points.add(RandomUtil.generator());
return;
public boolean hasLeftPoint(int currentPlayerPoint) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

각 점에서 우측으로 연결되는 선이 있는지 여부를 boolean으로 관리하다보니 좌측 연결선이 있는지, 우측 연결선이 있는지를 인덱스를 이용해서 확인해야 하네요.
각 점을 좌측, 우측, 연결 없음을 나타내는 클래스로 표현해보면 어떨까요?

if (currentPlayerPoint == 0) {
return false;
}
points.add(false);
return points.get(currentPlayerPoint - 1);
}

public boolean hasRightPoint(int currentPlayerPoint) {
if (points.size() == currentPlayerPoint || points.size() < currentPlayerPoint) {
return false;
}
return points.get(currentPlayerPoint);
}

public List<Boolean> getPoints() {
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/nextstep/ladder/domain/Player.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package nextstep.ladder.domain;

import java.util.Objects;

public class Player {

private static final String NAME_OVER_LENGTH_ERROR_TEXT = "사람에 이름을 최대5글자까지 가능합니다.";
Expand All @@ -17,4 +19,38 @@ public String getName() {
return name;
}

public int getPlayerResultIndex(int currentPoint, Ladder ladder) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사다리를 타고 내려가면서 최종 위치를 확인하는 것인 ladder 스스로 할 수 있지 않을까요?
ladder 클래스에 이 메서드를 구현해보면 어떨까요?

int point = currentPoint;
for (int index = 0; index < ladder.getHeight(); index++) {
point = calculationPoint(index, point, ladder);
}
return point;
}

private int calculationPoint(int lineIndex, int point, Ladder ladder) {
Line line = ladder.getLines().get(lineIndex);
if (line.hasLeftPoint(point)) {
return point - 1;
}
if (line.hasRightPoint(point)) {
return point + 1;
}
return point;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

Player player = (Player) o;

return Objects.equals(name, player.name);
}

@Override
public int hashCode() {
return name != null ? name.hashCode() : 0;
}

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

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Result {

private static final String RESULT_REX_PATTERN = ",";

private final List<String> values;

public Result(String values) {
this.values = Arrays.asList(values.split(RESULT_REX_PATTERN));
}

public String getValue(int index) {
return values.get(index);
}

public List<String> getValues() {
return Collections.unmodifiableList(values);
}

}
19 changes: 17 additions & 2 deletions src/main/java/nextstep/ladder/util/RandomUtil.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
package nextstep.ladder.util;

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

public class RandomUtil {

private static final Random random = new Random();
public static boolean generator() {
return random.nextBoolean();

public static List<Boolean> generatorPoints(int count) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메서드 이름이니 generatePoints와 같이 동사로 시작하는 이름을 사용하면 어떨까요?

List<Boolean> points = new ArrayList<>();
IntStream.range(0, count)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IntStream을 사용하신 이유가 있을까요?
for loop를 사용하는 것이 가독성이 더 좋을 것 같기도 합니다.
forEach 내부에서 generatorPoints의 지역변수인 points에 접근하는 것은 외부 효과를 유발하는 것이므로 지양하는 것이 좋습니다.

.forEach(index -> addPoint(points, index, random.nextBoolean()));
return points;
}

private static void addPoint(List<Boolean> points, int index, boolean point) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

포인트를 만드는 규칙은 Line에서 관리하거나, Point와 같은 클래스를 만들어서 관리해보면 어떨까요?
RandomUtil인데 단순히 난수를 생성하는 것이 아니라, 라인을 만들기 위한 규칙에 대해서 너무 자세히 알고 있는 것으로 보이네요.
RandomUtilgeneratorPoints, addPoint 등의 도메인 기능은 구현하지 말고, 임의의 boolean 값을 생성하는 기능만 제공하는 형태로 구현해보시면 좋겠습니다.

if (index == 0 || !points.get(index - 1)) {
points.add(point);
return;
}
points.add(false);
}

}
11 changes: 11 additions & 0 deletions src/main/java/nextstep/ladder/view/InputView.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ public class InputView {
private static final Scanner scanner = new Scanner(System.in);
private static final String INPUT_PLAYERS_NAME_TEXT = "참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)";
private static final String INPUT_LADDER_HEIGHT_TEXT = "최대 사다리 높이는 몇 개인가요?";
private static final String INPUT_RESULT_TEXT = "실행 결과를 입력하세요. (결과는 쉼표(,)로 구분하세요)";
private static final String INPUT_PLAYER_TEXT = "결과를 보고 싶은 사람은?";
public static String inputPlayers() {
System.out.println(INPUT_PLAYERS_NAME_TEXT);
return scanner.nextLine();
Expand All @@ -17,4 +19,13 @@ public static int inputLadderHeight() {
return Integer.parseInt(scanner.nextLine());
}

public static String inputResult() {
System.out.println(INPUT_RESULT_TEXT);
return scanner.nextLine();
}

public static String inputPlayer() {
System.out.println(INPUT_PLAYER_TEXT);
return scanner.nextLine();
}
}
Loading