Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
8c68e8f
feat: 초기 Application 생성
mgim9316-a11y Apr 28, 2026
c64a687
feat: 사다리의 높이 넒이 입력 받는 기능 추가
mgim9316-a11y Apr 28, 2026
8bec14e
feat: 사다리 전체의 정보를 담은 객체 생성
mgim9316-a11y Apr 28, 2026
9ab451e
feat: 랜덤값을 이용한 사다리를 값 설정 기능 추가
mgim9316-a11y Apr 28, 2026
75be806
feat: 사다리의 결과를 출력하는 기능 추가
mgim9316-a11y Apr 28, 2026
5c216cb
feat: 컨트롤러 기능 추가
mgim9316-a11y Apr 28, 2026
4416863
feat: 사다리의 높이를 관리하는 객체 생성
mgim9316-a11y Apr 28, 2026
96cdcde
feat: 사다리의 폭을 관리하는 객체 생성
mgim9316-a11y Apr 28, 2026
05c8532
feat: 사다리 생성하는 클래스 추가
mgim9316-a11y Apr 28, 2026
3813be0
feat: 사다리의 결과를 출력하는 기능 추가
mgim9316-a11y Apr 28, 2026
5f81de0
feat: 사다리의 규칙을 관리하는 기능 추가
mgim9316-a11y Apr 28, 2026
8242c8c
feat: 사다리의 결과를 출력하는 기능 추가
mgim9316-a11y Apr 28, 2026
7e90e30
feat: 사다리의 결과를 계산하는 기능 추가
mgim9316-a11y Apr 28, 2026
cca1c3f
feat: 사다리의 결과를 계산하는 기능 추가
mgim9316-a11y Apr 28, 2026
a12701e
feat: 사다리의 결과를 계산하는 기능 추가
mgim9316-a11y Apr 28, 2026
4444739
feat: 플레이어 객체 생성
mgim9316-a11y Apr 28, 2026
c2f8034
feat: 사다리에 매칭되는 결과 객체 생성
mgim9316-a11y Apr 28, 2026
c4a8b02
feat: 사람을 관리하는 일급 컬렉션 생성
mgim9316-a11y Apr 28, 2026
5644c84
feat: 결과를 관리하는 일급 컬렉션 생성
mgim9316-a11y Apr 28, 2026
160c1bd
feat: 결과와 이름을 저장하는 기능 추가
mgim9316-a11y Apr 28, 2026
821e9d2
feat:
mgim9316-a11y Apr 28, 2026
ea9a9a0
feat:
mgim9316-a11y Apr 28, 2026
3007e43
feat: 사다리 출력 메서드를 printLadderBoard 로 변경. 사다리 출력 전후로 사람 이름과 실행 결과를 6자리…
mgim9316-a11y Apr 28, 2026
9bd9cc1
feat: Map<Position, Position> 에서 객체를 키(Key)로 정상 조회하기 위해 equals 및 hash…
mgim9316-a11y Apr 28, 2026
035a320
feat: 시작 위치를 전달받아 매핑된 도착 위치를 반환하는메서드 추가.
mgim9316-a11y Apr 28, 2026
a38db34
feat: 결과를 출력하는 기능 추가
mgim9316-a11y Apr 28, 2026
b3cf1a4
feat: 사다리 게임 결과를 계산하는 기능 추가
mgim9316-a11y Apr 28, 2026
d03a637
chore: 공백 추가
mgim9316-a11y Apr 28, 2026
cec2e4f
feat: 해시코드, equals 추가
mgim9316-a11y May 1, 2026
b821da1
feat: 객체 비교로 수정
mgim9316-a11y May 1, 2026
ad72977
refactor: 검증 삭제
mgim9316-a11y May 1, 2026
ea59aca
refactor: 이름에 대한 책임 검증 분리
mgim9316-a11y May 1, 2026
895b46b
feat: 이름 입력 형식 검증 기능 추가
mgim9316-a11y May 1, 2026
3b5ff8d
refactor: 변수명 변경
mgim9316-a11y May 1, 2026
d9c9a92
refactor: ladder 의 기능 분리
mgim9316-a11y May 1, 2026
d91171f
refactor: 테스트 기능 수정
mgim9316-a11y May 1, 2026
0eb57b8
refactor: 테스트 기능 수정
mgim9316-a11y May 1, 2026
aebc5ed
refactor: 테스트 기능 수정
mgim9316-a11y May 1, 2026
c7eb9f2
refactor: 테스트 기능 수정
mgim9316-a11y May 1, 2026
f4c7986
feat: 테스트 기능 추가
mgim9316-a11y May 1, 2026
bea7a83
docs: 리드미 파일 작성
mgim9316-a11y May 1, 2026
f6bc97d
chore: 공백 추가
mgim9316-a11y May 1, 2026
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
42 changes: 42 additions & 0 deletions src/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# 사다리 게임

콘솔 환경에서 실행되는 사다리 타기 애플리케이션입니다. 사용자로부터 참여자 이름, 실행 결과, 사다리 높이를 입력받아 사다리를 무작위로 생성하고 결과를 계산합니다.

## 기능 목록

* **입력 기능**
* 참여할 사람의 이름을 쉼표(,)를 기준으로 입력받는다. (이름은 최대 5자)
* 게임 실행 결과를 쉼표(,)를 기준으로 입력받는다. (결과는 최대 5자, 참여자 수와 개수가 일치해야 함)
* 최대 사다리 높이를 입력받는다.
* 결과를 확인할 대상의 이름을 입력받는다. ('all' 입력 시 전체 결과 확인)

* **사다리 생성 로직**
* 주어진 참여자 수와 사다리 높이에 맞춰 사다리 라인을 생성한다.
* 가로 라인이 연속으로 겹치지 않도록 제어한다.
* 사다리 생성 시 사다리를 건너갈 수 없는(가로 라인이 하나도 없는) 수직 구간이 발생할 경우, 조건이 충족될 때까지 사다리를 재생성한다.

* **결과 계산 로직**
* 각 참여자의 시작 위치에서 사다리 라인을 타고 내려가 최종 도착 위치를 연산한다.
* 최종 도착 위치에 해당하는 실행 결과를 플레이어에게 매핑한다.

* **출력 기능**
* 생성된 사다리의 형태를 참여자 이름, 실행 결과와 함께 정렬하여 출력한다.
* 특정 플레이어의 이름을 입력하면 매핑된 실행 결과를 단건 출력한다.
* 'all'을 입력하면 모든 플레이어의 실행 결과를 전체 출력한다.

## 패키지 구조

* `controller`
* `LadderController` : 사용자 입력, 도메인 로직 실행, 뷰 출력 흐름을 제어합니다.
* `domain`
* `Ladder`, `Line` : 사다리의 형태 유지 및 이동 경로 데이터 관리를 담당합니다.
* `LadderGenerator`, `LineGenerator` : 무작위 값(Boolean)을 이용한 사다리 생성 책임을 가집니다.
* `Player`, `Players`, `Reward`, `Rewards` : 참여자와 결과에 대한 값 객체 및 일급 컬렉션입니다.
* `GameResult` : 사다리 이동 결과와 보상을 매핑하여 최종 게임 결과를 보관합니다.
* `view`
* `InputView` : 콘솔을 통한 데이터 입력을 담당합니다.
* `OutputView` : 사다리 형태 및 게임 결과 출력을 담당합니다.

## 실행 방법

`Application` 클래스의 `main` 메서드를 통해 애플리케이션을 실행합니다.
12 changes: 12 additions & 0 deletions src/main/java/Application.java
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();
}
}
65 changes: 65 additions & 0 deletions src/main/java/controller/LadderController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package controller;
import domain.LadderGenerator;
import domain.LadderHeight;
import domain.Players;
import domain.Rewards;
import domain.GameResult;
import domain.Ladder;
import domain.LadderWidth;
import domain.LadderResult;
import domain.RandomBooleanGenerator;
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 = LadderGenerator.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();
if (players.size() != rewards.size()) {
throw new IllegalArgumentException("사람 수와 결과 수가 일치하지 않습니다.");
}
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;
}
}
6 changes: 6 additions & 0 deletions src/main/java/domain/BooleanGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package domain;


public interface BooleanGenerator {
boolean generate();
}
38 changes: 38 additions & 0 deletions src/main/java/domain/GameResult.java
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);
}
}
35 changes: 35 additions & 0 deletions src/main/java/domain/Ladder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package domain;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class Ladder {
private final List<Line> lines;

public Ladder(List<Line> lines) {
this.lines = lines;
}

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);
}
}
45 changes: 45 additions & 0 deletions src/main/java/domain/LadderGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package domain;

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

public class LadderGenerator {

private LadderGenerator() {
}

public static Ladder generate(LadderWidth width, LadderHeight height, BooleanGenerator generator) {
Ladder ladder = createLadder(width, height, generator);
while (hasEmptyInterval(ladder, 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 static boolean hasEmptyInterval(Ladder ladder, LadderWidth width) {
return IntStream.range(0, width.getIntervalCount())
.anyMatch(index -> isEmptyInterval(ladder, index));
}

private static boolean isEmptyInterval(Ladder ladder, int index) {
return ladder.getLines().stream().noneMatch(line -> line.isConnectedAt(index));
}

}
24 changes: 24 additions & 0 deletions src/main/java/domain/LadderHeight.java
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;
}
}
15 changes: 15 additions & 0 deletions src/main/java/domain/LadderResult.java
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);
}
}
28 changes: 28 additions & 0 deletions src/main/java/domain/LadderWidth.java
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;
}

}
Loading