diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..f311dcb14 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,34 @@ +# 미션 - 지하철 노선도 경로 조회 미션 🚇 + +### 💻 기능 구현 목록 + +- 프로그램 시작 시, 사전 등록정보로 역, 노선, 구간정보 설정 + - `조건` 거리와 소요 시간은 Integer, 단위는 km와 분 +- 메인화면 기능 목록(1, Q)과 입력 안내 문구 출력 +- 원하는 메인 메뉴 입력 + - `예외` (1, Q) 외에 다른 값을 입력한 경우 + - `처리` 에러 문구([ERROR]) 출력 후 다시 입력 + +#### 1 경로 조회 +- 경로 조회 기능 목록(1, 2, B)과 입력 안내 문구 출력 +- 원하는 서브 메뉴 입력 + - `예외` (1, 2, B) 외에 다른 값을 입력한 경우 + - `처리` 에러 문구([ERROR]) 출력 후 다시 입력 +- 출발역 입력 안내 문구 출력 및 출발역 입력 + - `예외` 등록되지 않은 역인 경우 + - `처리` 에러 문구([ERROR]) 출력 후 다시 입력 +- 도착역 입력 안내 문구 출력 및 도착역 입력 + - `예외` 등록되지 않은 역인 경우 + - `처리` 에러 문구([ERROR]) 출력 후 다시 입력 + - `예외` 출발역과 도착역이 같은 경우 + - `예외` 출발역과 도착역이 연결되어 있지 않은 경우 + - `처리` 에러 문구([ERROR]) 출력 후 다시 입력 +- 경로 조회 + - `기준1` 최단거리 + - `기준2` 최소시간 +- 조회 결과 출력 + - `조건` 총 거리, 총 소요시간 함께 출력 + - 출력 후 메인화면으로 돌아감 + +#### Q 종료 +- 프로그램 종료 \ No newline at end of file diff --git a/src/main/java/subway/Application.java b/src/main/java/subway/Application.java index 0bcf786cc..bb22c7e09 100644 --- a/src/main/java/subway/Application.java +++ b/src/main/java/subway/Application.java @@ -1,10 +1,28 @@ package subway; +import subway.domain.*; +import subway.view.*; + import java.util.Scanner; public class Application { public static void main(String[] args) { final Scanner scanner = new Scanner(System.in); - // TODO: 프로그램 구현 + Init.initialize(); + startProgram(scanner); + } + + public static void startProgram(Scanner kbd) { + View.showMainMenu(); + String mainInput = InputView.inputFunction(kbd, Constants.MAIN_FUNCTIONS); + goSubMenu(mainInput, kbd); + } + + public static void goSubMenu(String input, Scanner kbd) { + System.out.println(); + if (input.equals(Constants.FIND_PATH)) + PathManage.managePath(kbd); + if (input.equalsIgnoreCase(Constants.FINISH_PROGRAM)) + View.finishProgram(); } } diff --git a/src/main/java/subway/PathManage.java b/src/main/java/subway/PathManage.java new file mode 100644 index 000000000..dcb183143 --- /dev/null +++ b/src/main/java/subway/PathManage.java @@ -0,0 +1,60 @@ +package subway; + +import subway.domain.*; +import subway.view.*; + +import java.util.List; +import java.util.Scanner; + +import static subway.Application.startProgram; + +public class PathManage { + public static void managePath(Scanner kbd) { + View.showPathMenu(); + String input = InputView.inputFunction(kbd, Constants.SUB_FUNCTIONS); + if (input.equals(Constants.MIN_DISTANCE)) + findMinDistance(kbd); + if (input.equals(Constants.MIN_TIME)) + findMinTime(kbd); + if (input.equals(Constants.GO_BACK_MENU)) + startProgram(kbd); + } + + public static void findMinDistance(Scanner kbd) { + try { + String[] stations = InputView.inputSrcDest(kbd); + List shortestPath = Init.dijkstraDistance.getPath(stations[0], stations[1]).getVertexList(); + showMinPath(Constants.PATH_DISTANCE, shortestPath, getMinValue(stations)); + startProgram(kbd); + } catch (Exception e) { + startProgram(kbd); + } + } + + public static void findMinTime(Scanner kbd) { + try { + String[] stations = InputView.inputSrcDest(kbd); + List shortestPath = Init.dijkstraTime.getPath(stations[0], stations[1]).getVertexList(); + showMinPath(Constants.PATH_TIME, shortestPath, getMinValue(stations)); + startProgram(kbd); + } catch (Exception e) { + startProgram(kbd); + } + } + + public static void showMinPath(String name, List path, int[] values) { + Line minPath = new Line(name); + LineRepository.addLine(minPath, path); + View.displayPath(minPath, values[0], values[1]); + LineRepository.deleteLineByName(name); + } + + public static int[] getMinValue(String[] stations) { + double time = Init.dijkstraTime.getPath(stations[0], stations[1]).getWeight(); + double distance = Init.dijkstraDistance.getPath(stations[0], stations[1]).getWeight(); + int[] values = new int[2]; + values[0] = (int) Math.round(time); + values[1] = (int) Math.round(distance); + return values; + } +} diff --git a/src/main/java/subway/domain/Constants.java b/src/main/java/subway/domain/Constants.java new file mode 100644 index 000000000..896c4d190 --- /dev/null +++ b/src/main/java/subway/domain/Constants.java @@ -0,0 +1,29 @@ +package subway.domain; + +import java.util.Arrays; +import java.util.List; + +public class Constants { + public static final List STATIONS = Arrays.asList("교대역", "강남역", "역삼역", "남부터미널역", "양재역", "양재시민의숲역", "매봉역"); + public static final String LINE_2 = "2호선"; + public static final List LINE_2_STATIONS = Arrays.asList("교대역", "강남역", "역삼역"); + public static final String LINE_3 = "3호선"; + public static final List LINE_3_STATIONS = Arrays.asList("교대역", "남부터미널역", "양재역", "매봉역"); + public static final String LINE_SINBUNDANG = "신분당선"; + public static final List LINE_SINBUNDANG_STATIONS = Arrays.asList("강남역", "양재역", "양재시민의숲역"); + public static final String PATH_DISTANCE = "DISTANCE"; + public static final String PATH_TIME = "TIME"; + + public static final int FUNCTION_INPUT_ERROR = 0; + public static final int NO_SUCH_NAME_ERROR = 1; + public static final int SAME_NAME_ERROR = 2; + public static final int UNKNOWN_ERROR = 3; + + public static final String FIND_PATH = "1"; + public static final String FINISH_PROGRAM = "Q"; + public static final List MAIN_FUNCTIONS = Arrays.asList(FIND_PATH, FINISH_PROGRAM); + public static final String MIN_DISTANCE = "1"; + public static final String MIN_TIME = "2"; + public static final String GO_BACK_MENU = "B"; + public static final List SUB_FUNCTIONS = Arrays.asList(MIN_DISTANCE, MIN_TIME, GO_BACK_MENU); +} diff --git a/src/main/java/subway/domain/Errors.java b/src/main/java/subway/domain/Errors.java new file mode 100644 index 000000000..8f330c471 --- /dev/null +++ b/src/main/java/subway/domain/Errors.java @@ -0,0 +1,28 @@ +package subway.domain; + +import subway.view.ErrorMessage; + +import java.util.List; + +public class Errors { + public static void checkInput(String input, List functions) { + if (!functions.contains(input)) { + ErrorMessage.displayErrorMessage(Constants.FUNCTION_INPUT_ERROR); + throw new IllegalArgumentException(); + } + } + + public static void checkExistStation(String name) { + if (!StationRepository.isExist(name)) { + ErrorMessage.displayErrorMessage(Constants.NO_SUCH_NAME_ERROR); + throw new IllegalArgumentException(); + } + } + + public static void checkSameName(String firstName, String lastName) { + if (firstName.equals(lastName)) { + ErrorMessage.displayErrorMessage(Constants.SAME_NAME_ERROR); + throw new IllegalArgumentException(); + } + } +} diff --git a/src/main/java/subway/domain/Init.java b/src/main/java/subway/domain/Init.java new file mode 100644 index 000000000..ea5fe2917 --- /dev/null +++ b/src/main/java/subway/domain/Init.java @@ -0,0 +1,77 @@ +package subway.domain; + +import org.jgrapht.alg.shortestpath.DijkstraShortestPath; +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.WeightedMultigraph; + + +public class Init { + public static DijkstraShortestPath dijkstraDistance; + public static DijkstraShortestPath dijkstraTime; + + public static void initialize() { + initializeStation(); + initializeLine(); + initializeTimeGraph(); + dijkstraDistance = initializeDistanceGraph(); + dijkstraTime = initializeTimeGraph(); + } + + public static void initializeStation() { + for (String name : Constants.STATIONS) + StationRepository.addStation(new Station(name)); + } + + public static void initializeLine() { + Line line2 = new Line(Constants.LINE_2); + LineRepository.addLine(line2, Constants.LINE_2_STATIONS); + Line line3 = new Line(Constants.LINE_3); + LineRepository.addLine(line3, Constants.LINE_3_STATIONS); + Line lineSinbundang = new Line(Constants.LINE_SINBUNDANG); + LineRepository.addLine(lineSinbundang, Constants.LINE_SINBUNDANG_STATIONS); + } + + public static DijkstraShortestPath initializeDistanceGraph() { + WeightedMultigraph graph = initializeGraph(); + int edges = 0; + for (Line line : LineRepository.lines()) + edges += line.getSize() - 1; + for (int i = 0; i < edges; i++) { + graph.setEdgeWeight(graph.addEdge("교대역", "강남역"), 2); + graph.setEdgeWeight(graph.addEdge("강남역", "역삼역"), 2); + graph.setEdgeWeight(graph.addEdge("교대역", "남부터미널역"), 3); + graph.setEdgeWeight(graph.addEdge("남부터미널역", "양재역"), 6); + graph.setEdgeWeight(graph.addEdge("양재역", "매봉역"), 1); + graph.setEdgeWeight(graph.addEdge("강남역", "양재역"), 2); + graph.setEdgeWeight(graph.addEdge("양재역", "양재시민의숲역"), 10); + } + DijkstraShortestPath dijkstraDistance = new DijkstraShortestPath(graph); + return dijkstraDistance; + } + + public static DijkstraShortestPath initializeTimeGraph() { + WeightedMultigraph graph = initializeGraph(); + int edges = 0; + for (Line line : LineRepository.lines()) + edges += line.getSize() - 1; + for (int i = 0; i < edges; i++) { + graph.setEdgeWeight(graph.addEdge("교대역", "강남역"), 3); + graph.setEdgeWeight(graph.addEdge("강남역", "역삼역"), 3); + graph.setEdgeWeight(graph.addEdge("교대역", "남부터미널역"), 2); + graph.setEdgeWeight(graph.addEdge("남부터미널역", "양재역"), 5); + graph.setEdgeWeight(graph.addEdge("양재역", "매봉역"), 1); + graph.setEdgeWeight(graph.addEdge("강남역", "양재역"), 8); + graph.setEdgeWeight(graph.addEdge("양재역", "양재시민의숲역"), 3); + } + DijkstraShortestPath dijkstraTime = new DijkstraShortestPath(graph); + return dijkstraTime; + } + + public static WeightedMultigraph initializeGraph() { + WeightedMultigraph graph + = new WeightedMultigraph(DefaultWeightedEdge.class); + for (String name : Constants.STATIONS) + graph.addVertex(name); + return graph; + } +} diff --git a/src/main/java/subway/domain/Line.java b/src/main/java/subway/domain/Line.java index f4d738d5a..00cd36447 100644 --- a/src/main/java/subway/domain/Line.java +++ b/src/main/java/subway/domain/Line.java @@ -1,7 +1,11 @@ package subway.domain; +import java.util.ArrayList; +import java.util.List; + public class Line { private String name; + private List stationNames = new ArrayList<>(); public Line(String name) { this.name = name; @@ -11,5 +15,17 @@ public String getName() { return name; } - // 추가 기능 구현 + public void addStation(int index, String name) { + stationNames.add(index-1, name); + } + + public void displayLine() { + for (String station : stationNames) + System.out.println("[INFO] " + station); + System.out.println(); + } + + public int getSize() { + return stationNames.size(); + } } diff --git a/src/main/java/subway/domain/LineRepository.java b/src/main/java/subway/domain/LineRepository.java index 2c4a723c9..680f30d24 100644 --- a/src/main/java/subway/domain/LineRepository.java +++ b/src/main/java/subway/domain/LineRepository.java @@ -12,7 +12,9 @@ public static List lines() { return Collections.unmodifiableList(lines); } - public static void addLine(Line line) { + public static void addLine(Line line, List names) { + for (int i = 1; i <= names.size(); i++) + line.addStation(i, names.get(i-1)); lines.add(line); } diff --git a/src/main/java/subway/domain/StationRepository.java b/src/main/java/subway/domain/StationRepository.java index 8ed9d103f..be95077ae 100644 --- a/src/main/java/subway/domain/StationRepository.java +++ b/src/main/java/subway/domain/StationRepository.java @@ -23,4 +23,8 @@ public static boolean deleteStation(String name) { public static void deleteAll() { stations.clear(); } + + public static boolean isExist(String name) { + return stations.stream().map(Station::getName).anyMatch(x -> x.equals(name)); + } } diff --git a/src/main/java/subway/view/ErrorMessage.java b/src/main/java/subway/view/ErrorMessage.java new file mode 100644 index 000000000..5a1134036 --- /dev/null +++ b/src/main/java/subway/view/ErrorMessage.java @@ -0,0 +1,16 @@ +package subway.view; + +import subway.domain.Constants; + +public class ErrorMessage { + public static void displayErrorMessage(int errorCase) { + if (errorCase == Constants.FUNCTION_INPUT_ERROR) + System.out.println("[ERROR] 선택할 수 없는 기능입니다."); + if (errorCase == Constants.NO_SUCH_NAME_ERROR) + System.out.println("[ERROR] 등록되지 않은 역 이름입니다."); + if (errorCase == Constants.SAME_NAME_ERROR) + System.out.println("[ERROR] 출발역과 도착역은 같을 수 없습니다."); + if (errorCase == Constants.UNKNOWN_ERROR) + System.out.println("[ERROR] 알 수 없는 오류가 발생했습니다."); + } +} diff --git a/src/main/java/subway/view/InputView.java b/src/main/java/subway/view/InputView.java new file mode 100644 index 000000000..c35f52e83 --- /dev/null +++ b/src/main/java/subway/view/InputView.java @@ -0,0 +1,36 @@ +package subway.view; + +import subway.domain.Errors; + +import java.util.List; +import java.util.Scanner; + +public class InputView { + public static String inputFunction(Scanner kbd, List functions) { + String input = "0"; + try { + System.out.println("\n## 원하는 기능을 선택하세요."); + input = kbd.nextLine(); + Errors.checkInput(input, functions); + } catch (Exception e) { + inputFunction(kbd, functions); + } + return input; + } + + public static String[] inputSrcDest(Scanner kbd) { + String[] stations = new String[2]; + try { + System.out.println("\n## 출발역을 입력하세요."); + stations[0] = kbd.nextLine(); + Errors.checkExistStation(stations[0]); + System.out.println("\n## 도착역을 입력하세요."); + stations[1] = kbd.nextLine(); + Errors.checkExistStation(stations[1]); + Errors.checkSameName(stations[0], stations[1]); + } catch (Exception e) { + inputSrcDest(kbd); + } + return stations; + } +} diff --git a/src/main/java/subway/view/View.java b/src/main/java/subway/view/View.java new file mode 100644 index 000000000..e7cd5066c --- /dev/null +++ b/src/main/java/subway/view/View.java @@ -0,0 +1,31 @@ +package subway.view; + +import subway.domain.Line; + +public class View { + public static void showMainMenu() { + System.out.println("\n## 메인 화면"); + System.out.println("1. 경로 조회"); + System.out.println("Q. 종료"); + } + + public static void showPathMenu() { + System.out.println("## 경로 기준"); + System.out.println("1. 최단 거리"); + System.out.println("2. 최소 시간"); + System.out.println("B. 돌아가기"); + } + + public static void displayPath(Line line, int time, int distance) { + System.out.println("\n## 조회 결과"); + System.out.println("[INFO] ---"); + System.out.println("[INFO] 총 거리: "+ distance + "km"); + System.out.println("[INFO] 총 소요 시간: "+ time + "분"); + System.out.println("[INFO] ---"); + line.displayLine(); + } + + public static void finishProgram() { + System.out.println("## 프로그램 종료"); + } +} diff --git a/src/test/java/subway/JGraphtTest.java b/src/test/java/subway/JGraphtTest.java index 4f5f76536..f911b0854 100644 --- a/src/test/java/subway/JGraphtTest.java +++ b/src/test/java/subway/JGraphtTest.java @@ -23,6 +23,7 @@ public void getDijkstraShortestPath() { DijkstraShortestPath dijkstraShortestPath = new DijkstraShortestPath(graph); List shortestPath = dijkstraShortestPath.getPath("v3", "v1").getVertexList(); + System.out.println(dijkstraShortestPath.getPath("v3", "v1").getWeight()); assertThat(shortestPath.size()).isEqualTo(3); } }