Skip to content

3단계 - 사다리(게임 실행) #2421

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

Merged
merged 7 commits into from
May 5, 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: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
# 사다리 게임


## 3단계 - 사다리(게임 실행)
### TODO
- [x] PR 반영
- [x] Line 객체에 가변 인자를 받은 생성자를 추가
- [x] LineGenerator가 generateLine에서 Line 객체를 리턴
- [x] LineGeneratorTest의 너무 많은 가변인자 테스트 정리->보다 적은수로 경우의 수를 커버
- [x] Ladder.java에 game Play에 대한 책임 부여 검토
- [x] Line.java에 LEFT, RIGHT, PASS와 같은 상태값을 enum으로 구현
- [x] Line의 index 원시값 포장
- [x] LineGenerator에 현재값,이전값을 가지는 Point 객체 도입 검토

- [x] 사다리 실행 결과를 출력해야 한다.
- [x] 개인별 이름을 입력하면 개인별 결과를 출력하고, "all"을 입력하면 전체 참여자의 실행 결과를 출력한다.

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



## 2단계 - 사다리 생성
### TODO
- [x] 사다리 실행 결과를 출력해야 한다.
- [x] 개인별 이름을 입력하면 개인별 결과를 출력하고, "all"을 입력하면 전체 참여자의 실행 결과를 출력한다.

- [x] PR 반영
- [x] 사다리 게임에 참여하는 사람에 이름을 최대5글자까지 부여할 수 있다. 사다리를 출력할 때 사람 이름도 같이 출력한다.
- [x] 사람 이름은 쉼표(,)를 기준으로 구분한다.
- [x] 사람 이름을 5자 기준으로 출력하기 때문에 사다리 폭도 넓어져야 한다.
Expand All @@ -11,6 +37,7 @@
### 프로그래밍 요구사항
- 자바 8의 스트림과 람다를 적용해 프로그래밍한다.
- 규칙 6: 모든 엔티티를 작게 유지한다.
- **규칙 7: 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.**


## 진행 방법
Expand Down
35 changes: 34 additions & 1 deletion src/main/java/nextstep/ladder/LadderGame.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,53 @@
package nextstep.ladder;

import nextstep.ladder.domain.ladder.Height;
import nextstep.ladder.domain.ladder.Index;
import nextstep.ladder.domain.ladder.Ladder;
import nextstep.ladder.domain.generator.LadderGenerator;
import nextstep.ladder.domain.name.Name;
import nextstep.ladder.domain.name.Names;
import nextstep.ladder.domain.reward.Reward;
import nextstep.ladder.domain.reward.Rewards;
import nextstep.ladder.view.InputView;
import nextstep.ladder.view.ResultView;

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

public class LadderGame {

public static void main(String[] args) {
Copy link
Contributor

Choose a reason for hiding this comment

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

사다리 타기의 전체 흐름을 다음과 같은 구조로 구현해 보는 것은 어떨까?
main()은 MVC에서 Controller의 역할로 가능한 가볍게 구현해야 한다.
Controller 내에는 로직은 최대한 배제하고 Model과 View 사이의 연결 역할만 하도록 구현한다.

public static void main(String[] args) throws Exception {
    Players players = InputView.createPlayers();
    Rewards rewards = InputView.createRewards();

    Ladder ladder = LadderFactory.createLadder(players.countOfPeople(), InputView.getHeight());
    OutputView.printLadder(players, ladder, rewards);

    MatchingResult matchingResult = ladder.play();
    LadderResult result = matchingResult.map(players, rewards);

    OutputView.printResult(result);
}

MatchingResult는 Map<Integer, Integer>에 대한 일급콜렉션 객체이다.
Map의 key는 시작 위치, value는 사다리를 실행한 결과 위치이다.

LadderResult는 위치 값만 가지고 있는 MatchingResult의 값을 이름과 결과로 매핑한 결과를 가지는 일급콜렉션 객체이다.

반드시 위와 같은 구조로 개발하지 않아도 된다.
단, 위 코드 힌트를 참고해 개선할 부분이 있으면 개선해 본다.

Names names = new Names(InputView.getNames());
Height height = new Height(InputView.getHeight());
Rewards rewards = new Rewards(InputView.getRewards());

Ladder ladder = LadderGenerator.generateLadder(names.count(), height);

ResultView.result(names, ladder);
ResultView.ladderResult(names, ladder, rewards);

while(true) {
String playerName = InputView.getPlayerName();

if (playerName.equalsIgnoreCase("quit")) {
break;
}

if (playerName.equalsIgnoreCase("all")) {
List<Integer> rewardList = ladder.all().stream()
.map(Index::value)
.collect(Collectors.toList());
ResultView.rewardResult(names, rewards, rewardList);
continue;
}

Name name = new Name(playerName);
Index rewardIdx = ladder.resultOf(names.positionOf(name));
try {
Reward reward = rewards.get(rewardIdx.value());
System.out.println(reward);
} catch(IndexOutOfBoundsException e) {
System.out.println("이름이 명단에 없습니다.");
}
}
}
}
34 changes: 34 additions & 0 deletions src/main/java/nextstep/ladder/domain/common/AbstractValueList.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package nextstep.ladder.domain.common;

import java.util.List;

public abstract class AbstractValueList<T> {
protected final List<T> values;

protected AbstractValueList(List<T> values) {
if (values == null || values.isEmpty()) {
throw new IllegalArgumentException("리스트는 비어 있을 수 없습니다.");
}
this.values = List.copyOf(values);
}

public List<T> values() {
return values;
}

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

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

public int positionOf(Object value) {
if (value == null) {
throw new IllegalArgumentException("찾는 값은 null일 수 없습니다.");
}

return values.indexOf(value);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
import nextstep.ladder.domain.ladder.Line;

public class LadderGenerator {
public static Ladder generateLadder(int countOfPerson, Height height) {
Ladder ladder = new Ladder();
public static Ladder generateLadder(int countOfPeople, Height height) {
Ladder ladder = new Ladder(countOfPeople);
for (int i = 0; i < height.value(); i++) {
Line line = new Line(countOfPerson);
Line line = new Line(countOfPeople);
ladder.add(line);
}

Expand Down
44 changes: 44 additions & 0 deletions src/main/java/nextstep/ladder/domain/generator/LineGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package nextstep.ladder.domain.generator;

import nextstep.ladder.domain.ladder.Line;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class LineGenerator {
public static Line generateLine(int countOfPerson) {
List<Boolean> line = new ArrayList<>();
Point point = new Point(false, false);

for (int i = 0; i < countOfPerson - 1; i++) {
boolean nextBoolean = randomBoolean();
point = new Point(point.next(), nextBoolean);

if (point.isConsecutive()) {
nextBoolean = false;
point = new Point(point.current(), false);
}

line.add(nextBoolean);
}
return new Line(line);
}

public static void assertValidLine(List<Boolean> values) {
if (values == null || values.isEmpty()) {
throw new IllegalArgumentException("사다리에는 빈값이 올 수 없습니다. values=" + values);
}

for(int i = 0; i < values.size() -1; i++) {
Point point = new Point(values.get(i), values.get(i + 1));
if(point.isConsecutive()) {
throw new IllegalArgumentException("연속된 사다리 계단은 나올 수 없습니다.");
}
}
}

private static boolean randomBoolean() {
return ThreadLocalRandom.current().nextBoolean();
}
}
23 changes: 23 additions & 0 deletions src/main/java/nextstep/ladder/domain/generator/Point.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package nextstep.ladder.domain.generator;

public class Point {
private final boolean current;
private final boolean next;

public Point(boolean current, boolean next) {
Copy link
Contributor

Choose a reason for hiding this comment

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

두 개의 값이 true, true인 경우는 있을 수 없기 때문에 이 경우 생성자에서 예외 처리하는 것은 어떨까?

this.current = current;
this.next = next;
}

public boolean isConsecutive() {
return current && next;
}

public boolean current() {
return current;
}

public boolean next() {
return next;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
}
public static Point first(boolean current) {
return new Point(false, current);
}
public Point next(boolean current) {
return new Point(this.current, current);
}

next가 아니라 previous 값을 가지는 구조로 구현하는 것은 어떨까?
그럴 경우 위와 같이 구현할 수 있지 않을까?

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

public enum Direction {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

LEFT {
@Override
public Index move(Index index) {
return index.decrement();
}
},
RIGHT {
@Override
public Index move(Index index) {
return index.increment();
}
},
PASS {
@Override
public Index move(Index index) {
return index;
}
};

public abstract Index move(Index index);
}
43 changes: 43 additions & 0 deletions src/main/java/nextstep/ladder/domain/ladder/Index.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package nextstep.ladder.domain.ladder;

public class Index {
private final int value;
private final int max;

public Index(int value, int max) {
validate(value, max);
this.value = value;
this.max = max;
}

private void validate(int value, int max) {
if (value < 0 || value >= max) {
throw new IllegalArgumentException("인덱스는 0 이상 " + (max - 1) + " 이하여야 합니다.");
}
}

public int value() {
return value;
}

public Index increment() {
return new Index(value + 1, max);
}

public Index decrement() {
return new Index(value - 1, max);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Index index = (Index) o;
return value == index.value && max == index.max;
}

@Override
public int hashCode() {
return value;
}
}
34 changes: 26 additions & 8 deletions src/main/java/nextstep/ladder/domain/ladder/Ladder.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,35 @@
import java.util.List;

public class Ladder {
List<Line> lineList;
private final List<Line> lineList;
private final int countOfPeople;

public Ladder() {
public Ladder(int countOfPeople) {
this.countOfPeople = countOfPeople;
this.lineList = new ArrayList<>();
}

public List<Line> values() {
return lineList;
}
public List<Line> values() {
return lineList;
}

public void add(Line line) {
lineList.add(line);
}

public void add(Line line) {
lineList.add(line);
}
public Index resultOf(int position) {
Index index = new Index(position, countOfPeople);
for(Line line: values()) {
index = line.movePerson(index);
}
return index;
}

public List<Index> all() {
List<Index> results = new ArrayList<>();
for(int i = 0; i < countOfPeople; i++) {
results.add(resultOf(i));
}
return results;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Ladder가 사다리 타기를 위한 모든 상태 값을 가진다.
Ladder에 play()와 같은 메시지를 보내 사다리 타기를 실행하는 역할을 부여하면 어떨까?

}
Loading