Skip to content

Commit 2fe4d18

Browse files
authored
고생하셨습니다.
🎉 PR 머지 완료! 🎉
1 parent fa13b03 commit 2fe4d18

30 files changed

+1244
-0
lines changed

README.md

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# 사다리 게임(Ladder Game)
2+
3+
## 🎯 게임 개요
4+
5+
이 게임은 참여 인원과 실행 결과를 입력하여 개인별 결과를 출력해주는 사다리 게임입니다!
6+
7+
## 📌 게임 진행 방식
8+
9+
- **참여 인원 입력**: 참여 인원의 이름을 입력해주세요. (이름은 쉼표(,)를 기준으로 구분하고 최대 5글자)
10+
- **실행 결과 입력**: 원하는 실행 결과를 입력해주세요. (쉼표(,)를 기준으로 구분, 예시-꽝, 5000 등)
11+
- **사다리 높이 입력**: 원하는 사다리 높이를 입력해주세요.
12+
- **전체 결과 발표**: 전체 사다리 결과가 나옵니다.
13+
- **지목 결과 발표**: 지목한 사람의 실행 결과를 확인합니다. (예-tommy or all)
14+
15+
## 🚀 실행 방법
16+
17+
1. 프로그램을 실행합니다.
18+
2. 참여 인원과 실행 결과를 입력합니다.
19+
3. 사다리 높이를 입력해 사다리를 만듭니다.
20+
4. 궁금한 사람의 이름을 지목하여 그 결과를 확인하세요! 🎉
21+
22+
## 🛠 주요 기능
23+
24+
- **사다리 생성**: 원하는 높이의 사다리 생성.
25+
- **결과 계산**: 각 인원의 사다리 타기 결과 계산.
26+
27+
## ✅ 체크리스트
28+
29+
### 요구사항
30+
- [v] 게임 개요 및 진행 방식 정리
31+
- [v] 필요한 기능 도출 (입력, 번호 생성, 결과 처리)
32+
33+
### 리팩토링
34+
- [v] enum 클래스 이용
35+
- [v] 매직 넘버 제거
36+
- [v] indent depth 1까지 허용
37+
- [v] 일급 컬렉션 사용
38+
- [v] 함수형 문법 사용
39+
- [v] 모든 엔티티 작게 유지
40+
41+
### 테스트
42+
- [v] 정상 입력값에 대한 동작 확인
43+
- [v] 잘못된 입력값 처리 테스트
44+
- [v] 사다리 생성 동작 확인
45+
- [v] 결과값 확인
46+

src/main/java/Application.java

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import controller.LadderController;
2+
import util.RandomLadderGenerator;
3+
import view.InputView;
4+
import view.OutputView;
5+
6+
public class Application {
7+
8+
public static void main(String[] args) {
9+
InputView inputView = new InputView();
10+
OutputView outputView = new OutputView();
11+
RandomLadderGenerator generator = new RandomLadderGenerator();
12+
LadderController controller = new LadderController(inputView, outputView, generator);
13+
controller.run();
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package controller;
2+
3+
import static view.InputView.INPUT_EXCEPTION_MESSAGE;
4+
5+
import java.util.Map;
6+
import java.util.stream.Collectors;
7+
import model.goal.Goal;
8+
import model.goal.Goals;
9+
import model.ladder.Ladder;
10+
import model.LadderGame;
11+
import model.ladder.LadderHeight;
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
import model.player.Player;
15+
import model.player.PlayerName;
16+
import model.player.Players;
17+
import model.player.Position;
18+
import util.LadderGenerator;
19+
import view.InputView;
20+
import view.OutputView;
21+
22+
public class LadderController {
23+
24+
private final InputView inputView;
25+
private final OutputView outputView;
26+
private final LadderGenerator generator;
27+
28+
public LadderController(InputView inputView, OutputView outputView, LadderGenerator generator) {
29+
this.inputView = inputView;
30+
this.outputView = outputView;
31+
this.generator = generator;
32+
}
33+
34+
public void run() {
35+
Players players = createPlayers();
36+
Goals goals = createGoals(players);
37+
LadderHeight height = createLadderHeight(players.size());
38+
Ladder ladder = Ladder.of(players, height, generator);
39+
LadderGame game = new LadderGame(ladder);
40+
Map<Player, Goal> results = game.play(players, goals);
41+
outputView.printLadder(ladder, players, goals);
42+
showResults(players, results);
43+
}
44+
45+
private Players createPlayers() {
46+
try {
47+
List<String> rawNames = inputView.inputPlayers();
48+
return Players.from(rawNames);
49+
} catch (IllegalArgumentException e) {
50+
outputView.printErrorMessage(e.getMessage());
51+
return createPlayers();
52+
}
53+
}
54+
55+
private Goals createGoals(Players players) {
56+
try {
57+
List<String> rawGoals = inputView.inputGoals();
58+
return Goals.from(rawGoals, players.size());
59+
} catch (IllegalArgumentException e) {
60+
outputView.printErrorMessage(e.getMessage());
61+
return createGoals(players);
62+
}
63+
}
64+
65+
private LadderHeight createLadderHeight(int playerCount) {
66+
try {
67+
int height = inputView.inputLadderHeight();
68+
return new LadderHeight(height, playerCount);
69+
} catch (IllegalArgumentException e) {
70+
outputView.printErrorMessage(e.getMessage());
71+
return createLadderHeight(playerCount);
72+
}
73+
}
74+
75+
private void showResults(Players players, Map<Player, Goal> results) {
76+
String input = inputView.inputPlayerForResult();
77+
while (!input.isBlank()) {
78+
input = processResultInput(input, players, results);
79+
}
80+
}
81+
82+
private String processResultInput(String input, Players players, Map<Player, Goal> results) {
83+
try {
84+
selectResult(input, players, results);
85+
return inputView.inputPlayerForResult();
86+
} catch (IllegalArgumentException e) {
87+
outputView.printErrorMessage(e.getMessage());
88+
return inputView.inputPlayerForResult();
89+
}
90+
}
91+
92+
private void selectResult(String input, Players players, Map<Player, Goal> results) {
93+
if (input.equals("all")) {
94+
outputView.printAllResults(results);
95+
return;
96+
}
97+
players.validateContainsPlayer(input);
98+
outputView.printSingleResult(input, players, results);
99+
}
100+
}

src/main/java/model/LadderGame.java

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package model;
2+
3+
import java.util.Map;
4+
import java.util.stream.Collectors;
5+
import java.util.stream.IntStream;
6+
import model.ladder.Ladder;
7+
import model.player.Position;
8+
import model.goal.Goal;
9+
import model.goal.Goals;
10+
import model.player.Player;
11+
import model.player.Players;
12+
13+
public class LadderGame {
14+
15+
private final Ladder ladder;
16+
17+
public LadderGame(Ladder ladder) {
18+
this.ladder = ladder;
19+
}
20+
21+
public Map<Player, Goal> play(Players players, Goals goals) {
22+
return IntStream.range(0, players.size())
23+
.boxed()
24+
.collect(Collectors.toMap(
25+
players::getPlayerAt,
26+
index -> goals.getGoalAt(ladder.getGoalsPosition(new Position(index)).getValue())
27+
));
28+
}
29+
}

src/main/java/model/goal/Goal.java

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package model.goal;
2+
3+
public class Goal {
4+
5+
private final int MAXINUM_GOAL_LENGTH = 5;
6+
private final String goal;
7+
8+
public Goal(String goal) {
9+
validateGoal(goal);
10+
this.goal = goal;
11+
}
12+
13+
private void validateGoal(String goal) {
14+
if (goal == null || goal.isBlank() || goal.length() > MAXINUM_GOAL_LENGTH) {
15+
throw new IllegalArgumentException("실행 결과는 1~5글자여야 합니다.");
16+
}
17+
18+
}
19+
20+
public String getGoal() {
21+
return goal;
22+
}
23+
}

src/main/java/model/goal/Goals.java

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package model.goal;
2+
3+
import java.util.List;
4+
5+
public class Goals {
6+
7+
private final List<Goal> goals;
8+
9+
public Goals(List<Goal> goals, int playerCount) {
10+
validateGoals(goals, playerCount);
11+
this.goals = List.copyOf(goals);
12+
13+
}
14+
15+
public static Goals from(List<String> rawGoals, int playerCount) {
16+
List<Goal> goalList = rawGoals.stream()
17+
.map(Goal::new)
18+
.toList();
19+
return new Goals(goalList, playerCount);
20+
}
21+
22+
private void validateGoals(List<Goal> goals, int playerCount) {
23+
if (goals.size() != playerCount) {
24+
throw new IllegalArgumentException("실행 결과 수와 참여자 수는 같아야 합니다.");
25+
}
26+
}
27+
28+
public Goal getGoalAt(int index) {
29+
if (index < 0 || index >= goals.size()) {
30+
throw new IllegalArgumentException("유효하지 않은 인덱스입니다: " + index);
31+
}
32+
return goals.get(index);
33+
}
34+
35+
public List<Goal> getGoals() {
36+
return List.copyOf(goals);
37+
}
38+
}
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package model.ladder;
2+
3+
import java.util.List;
4+
import model.player.Position;
5+
6+
public enum Direction {
7+
8+
// 현재 위치의 오른쪽에 사다리 연결이 있을 경우 오른쪽 이동
9+
RIGHT(
10+
(position, points) -> position.getValue() < points.size()
11+
&& points.get(position.getValue()).isConnected(),
12+
Position::moveToRight
13+
),
14+
// 현재 위치의 왼쪽에 사다리 연결이 있을 경우 왼쪽 이동
15+
LEFT(
16+
(position, points) -> position.getValue() > 0
17+
&& points.get(position.getValue() - 1).isConnected(),
18+
Position::moveToLeft
19+
),
20+
// 그 외의 경우, 현재 위치에 그대로 머무름
21+
STAY(
22+
(position, points) -> true,
23+
position -> position
24+
);
25+
26+
private final MoveStrategy strategy;
27+
private final PositionChanger changer;
28+
29+
Direction(MoveStrategy strategy, PositionChanger changer) {
30+
this.strategy = strategy;
31+
this.changer = changer;
32+
}
33+
34+
public boolean isMovable(Position position, List<Point> points) {
35+
return strategy.isMovable(position, points);
36+
}
37+
38+
public Position move(Position position) {
39+
return changer.move(position);
40+
}
41+
42+
@FunctionalInterface
43+
interface MoveStrategy { // 지금 이 방향으로 움직일 수 있는가?
44+
boolean isMovable(Position position, List<Point> points);
45+
}
46+
47+
@FunctionalInterface
48+
interface PositionChanger { // 이동했을 때 Position 어디인가?
49+
Position move(Position position);
50+
}
51+
}
+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package model.ladder;
2+
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.List;
6+
import java.util.stream.Collectors;
7+
import java.util.stream.IntStream;
8+
import model.player.Players;
9+
import model.player.Position;
10+
import util.LadderGenerator;
11+
12+
public class Ladder {
13+
14+
private final LadderHeight height;
15+
private final List<Line> lines;
16+
17+
public Ladder(LadderHeight height, List<Line> lines) {
18+
this.height = height;
19+
this.lines = lines;
20+
}
21+
22+
public static Ladder of(Players players, LadderHeight height, LadderGenerator ladderGenerator) {
23+
List<Line> lines = new ArrayList<>();
24+
Ladder ladder = new Ladder(height, lines);
25+
ladder.addLines(players.size(), ladderGenerator);
26+
return ladder;
27+
}
28+
29+
private void addLines(int playersCount, LadderGenerator ladderGenerator) {
30+
for (int i = 0; i < getLadderHeight(); i++) {
31+
lines.add(Line.create(playersCount, ladderGenerator));
32+
}
33+
if (isLadderNotConnected(playersCount)) {
34+
lines.clear();
35+
addLines(playersCount, ladderGenerator);
36+
}
37+
}
38+
39+
// Line이 최소 한 번 연결되었는지 검증
40+
private boolean isLadderNotConnected(int playersCount) {
41+
Boolean[] isConnectedAt = getLadderConnectionStatus(playersCount);
42+
return Arrays.stream(isConnectedAt)
43+
.collect(Collectors.toSet())
44+
.size() != 1;
45+
}
46+
47+
// 전체 Line별 연결 상태 확인
48+
private Boolean[] getLadderConnectionStatus(int playersCount) {
49+
int width = playersCount - 1;
50+
Boolean[] isConnectedAt = new Boolean[width];
51+
Arrays.fill(isConnectedAt, false);
52+
lines.forEach(line -> getLineConnectionStatus(isConnectedAt, line));
53+
return isConnectedAt;
54+
}
55+
56+
// 특정 Line의 연결 상태 확인
57+
private void getLineConnectionStatus(Boolean[] isConnectedAt, Line line) {
58+
IntStream.range(0, line.getLadderWidth())
59+
.forEach(i -> getPointConnectionStatus(isConnectedAt, line, i));
60+
}
61+
62+
// 특정 Line의 특정 Point 연결 상태 확인
63+
private void getPointConnectionStatus(Boolean[] isConnectedAt, Line line, int i) {
64+
if (line.getPoints().get(i).isConnected()) {
65+
isConnectedAt[i] = true;
66+
}
67+
}
68+
69+
public int getLadderHeight() {
70+
return height.getLadderHeight();
71+
}
72+
73+
public int getLadderWidth(Players players) {
74+
return players.size();
75+
}
76+
77+
public Position getGoalsPosition(Position start) {
78+
Position current = start;
79+
for (Line line : lines) {
80+
current = line.move(current);
81+
}
82+
return current;
83+
}
84+
85+
public List<Line> getLines() {
86+
return List.copyOf(lines);
87+
}
88+
}

0 commit comments

Comments
 (0)