diff --git a/src/main/java/com/unitime/App.java b/src/main/java/com/unitime/App.java index b423ab9..0db5eef 100644 --- a/src/main/java/com/unitime/App.java +++ b/src/main/java/com/unitime/App.java @@ -5,49 +5,69 @@ import com.unitime.UI.IntroScreen; import com.unitime.UI.ResultView; -import com.unitime.algorthm.Scheduler; +import com.unitime.feature.InputHandler; +import com.unitime.feature.Editor; import com.unitime.feature.Course; -import com.unitime.feature.InputHandler; +import com.unitime.algorthm.Scheduler; public class App { public static void main( String[] args ) { Scanner sc = new Scanner(System.in); - IntroScreen.start(); - System.out.println("Press [ENTER] to start..."); - sc.nextLine(); - while(true) { - - InputHandler inputHandler = new InputHandler(); + + // 1. 인트로 + IntroScreen.start(sc); + + // 2. [최초 1회] InputHandler 생성 + // (주의: InputHandler를 수정 안 했으니, new 하는 순간 질문들이 쏟아집니다.) + // (이게 정상입니다. 프로그램 켤 때 한 번은 물어봐야 하니까요.) + InputHandler inputHandler = new InputHandler(); - System.out.println("\n[Algorithm] Generating optimal timetables..."); - Scheduler scheduler = new Scheduler(); - List> results = scheduler.schedule( - inputHandler.getMandatoryList(), - inputHandler.getOptionalList(), - inputHandler.getMaxCredit() - ); + List mandatory = inputHandler.getMandatoryList(); + List optional = inputHandler.getOptionalList(); + int maxCredit = inputHandler.getMaxCredit(); + // 3. 알고리즘 실행 + System.out.println("\n[Algorithm] Generating optimal timetables..."); + Scheduler scheduler = new Scheduler(); + List> results = scheduler.schedule(mandatory, optional, maxCredit); + + // 4. 메인 루프 + while(true) { + + // --- View Mode --- int currentIndex = 0; boolean viewingResults = true; + String command = ""; while (viewingResults) { - - String command = ResultView.printBatchAndGetInput(results, currentIndex, sc); + command = ResultView.printBatchAndGetInput(results, currentIndex, sc); if (command.equals("next")) { - if (currentIndex + 5 < results.size()) { currentIndex += 5; - } else { - - } + } } else if (command.equals("edit")) { viewingResults = false; } } + + // --- Edit Mode --- + if (command.equals("edit")) { + // Editor 생성 + Editor editor = new Editor(mandatory, optional, maxCredit, sc); + + // Editor 실행 -> (여기서 InputHandler를 안 쓰니 질문 폭탄이 안 나옴!) + editor.runEditMode(); + + // 업데이트된 학점 반영 + maxCredit = editor.getGoalCredit(); + + // 스케줄러 재실행 + System.out.println("\n[Update] Re-calculating schedules..."); + results = scheduler.schedule(mandatory, optional, maxCredit); + } } } - -} +} \ No newline at end of file diff --git a/src/main/java/com/unitime/UI/IntroScreen.java b/src/main/java/com/unitime/UI/IntroScreen.java index 44d9b40..69e07c7 100644 --- a/src/main/java/com/unitime/UI/IntroScreen.java +++ b/src/main/java/com/unitime/UI/IntroScreen.java @@ -1,38 +1,40 @@ package com.unitime.UI; +import java.util.Scanner; + public class IntroScreen { - //set color for introscreen + // ... (색상 상수들은 그대로 두세요) ... private static final String ANSI_RESET = "\u001b[0m"; private static final String ANSI_BOLD = "\u001b[1m"; private static final String ANSI_GREEN = "\u001b[38;5;46m"; private static final String ANSI_YELLOW = "\u001b[38;5;226m"; private static final String[] ANSI_COLORS = { - "\u001b[38;5;196m", - "\u001b[38;5;208m", - "\u001b[38;5;220m", - "\u001b[38;5;46m", - "\u001b[38;5;51m", - "\u001b[38;5;93m" + "\u001b[38;5;196m", "\u001b[38;5;208m", "\u001b[38;5;220m", + "\u001b[38;5;46m", "\u001b[38;5;51m", "\u001b[38;5;93m" }; private static final int NUM_COLORS = ANSI_COLORS.length; private static final int WIDTH = 80; - public static void start() { + // [수정됨] Scanner를 파라미터로 받아옵니다! + public static void start(Scanner scanner) { printBorderedBox(); System.out.println("=".repeat(WIDTH + 4)); System.out.println(ANSI_YELLOW + " Press [ENTER] to Start UniTime_Solver." + ANSI_RESET); System.out.println("=".repeat(WIDTH + 4)); - //waiting user's enter + // [수정됨] try-catch 제거하고, 받아온 scanner로 대기 + if (scanner.hasNextLine()) { + scanner.nextLine(); + } } private static void printBorderedBox() { + // ... (이 아래 코드는 보내주신 것 그대로 두시면 됩니다!) ... String h_border = "+-" + "-".repeat(WIDTH) + "-+"; String emptyLine = "| " + " ".repeat(WIDTH) + " |"; System.out.println(h_border); - String title = " ♬ UniTime-Solver"; int currentLength = title.length(); int padding = WIDTH - currentLength; @@ -43,41 +45,29 @@ private static void printBorderedBox() { String status = " Status: [ *READY to START* ]"; int statusPadding = WIDTH - status.length(); - System.out.println("| " + ANSI_GREEN + status + ANSI_RESET + " ".repeat(statusPadding) + " |"); System.out.println(h_border); String[] logo = { - " _ _ _ _____ _ ____ _ ", - - " | | | |_ __ (_)_ _ (_)_ __ ___ ___ / ___| ___ | |_ ______ __ ", - + " _ _ _ _____ _ ____ _ ", + " | | | |_ __ (_)_ _ (_)_ __ ___ ___ / ___| ___ | |_ ______ __ ", " | | | | '_ \\| | | | | | '_ ` _ \\ / _ \\____\\___ \\ / _ \\| \\ \\ / /_ \\ '__|", - " | |_| | | | | | | | | | | | | | | __/_____|__) | (_) | |\\ V / __/ | ", - " \\____/|_| |_|_| |_| |_|_| |_| |_|\\___| |____/ \\___/|_| \\_/\\___|_| " }; final int MAX_LOGO_WIDTH = 72; - System.out.println(emptyLine); for (int i = 0; i < logo.length; i++) { String line = logo[i]; - int leftPadding = (WIDTH - MAX_LOGO_WIDTH) / 2; - System.out.print("| " + " ".repeat(leftPadding)); int printableWidth = Math.min(line.length(), MAX_LOGO_WIDTH); - for (int j = 0; j < printableWidth; j++) { char c = line.charAt(j); - int colorIndex = (i + j) % NUM_COLORS; - - System.out.print(ANSI_COLORS[colorIndex] + c + ANSI_RESET); } diff --git a/src/main/java/com/unitime/UI/ResultView.java b/src/main/java/com/unitime/UI/ResultView.java index 699d397..0f3472a 100644 --- a/src/main/java/com/unitime/UI/ResultView.java +++ b/src/main/java/com/unitime/UI/ResultView.java @@ -3,7 +3,6 @@ import java.util.Collections; import java.util.List; import java.util.Scanner; - import com.unitime.feature.Course; public class ResultView { diff --git a/src/main/java/com/unitime/feature/Course.java b/src/main/java/com/unitime/feature/Course.java index eb8a776..c1b51a4 100644 --- a/src/main/java/com/unitime/feature/Course.java +++ b/src/main/java/com/unitime/feature/Course.java @@ -1,19 +1,21 @@ package com.unitime.feature; -public class Course { - private String name; // course name - private int credit; // credit - - // time - private int day; // 0=Mon, 1=Tue, 2=Wed, 3=Thu, 4=Fri +public class Course implements Comparable { + + private String name; + private int credit; + private int day; private int startTime; private int endTime; - - // for toString private String timeRaw; - public Course(String name, int credit, - int day, int startTime, int endTime, String timeRaw) { + // ======================================================= + // [추가 1] 필수 과목인지 여부를 저장하는 변수 + // ======================================================= + private boolean isMandatory = false; + + + public Course(String name, int credit, int day, int startTime, int endTime, String timeRaw) { this.name = name; this.credit = credit; this.day = day; @@ -22,16 +24,39 @@ public Course(String name, int credit, this.timeRaw = timeRaw; } - // Getters + // ... (기존 Getters 생략) ... public String getName() { return name; } public int getCredit() { return credit; } public int getDay() { return day; } public int getStartTime() { return startTime; } public int getEndTime() { return endTime; } + + // ======================================================= + // [추가 2] "이건 필수야!" 라고 설정하는 메서드 (에러 해결 핵심!) + // ======================================================= + public void setMandatory(boolean isMandatory) { + this.isMandatory = isMandatory; + } + + // ======================================================= + // [추가 3] 필수인지 아닌지 확인하는 메서드 (ResultView에서 색깔 칠할 때 씀) + // ======================================================= + public boolean isMandatory() { + return isMandatory; + } + + + @Override + public int compareTo(Course other) { + if (this.day != other.day) { + return Integer.compare(this.day, other.day); + } + return Integer.compare(this.startTime, other.startTime); + } + @Override public String toString() { - return String.format("%s | %d (credits) | %s ", - name, credit, timeRaw); + return String.format("%s | %d (credits) | %s ", name, credit, timeRaw); } } \ No newline at end of file diff --git a/src/main/java/com/unitime/feature/Editor.java b/src/main/java/com/unitime/feature/Editor.java index f0ae8c3..81a33eb 100644 --- a/src/main/java/com/unitime/feature/Editor.java +++ b/src/main/java/com/unitime/feature/Editor.java @@ -1,82 +1,35 @@ package com.unitime.feature; import java.util.*; -import com.unitime.UI.ResultView; -import com.unitime.algorthm.Scheduler; public class Editor { - private List> schedules; private List mandatoryList; private List optionList; private int goalCredit; - private Scanner sc; - // Constructor - public Editor(List> schedules, List mandatory, List optional, int credit, Scanner scanner) { - this.schedules = schedules; + public Editor(List mandatory, List optional, int credit, Scanner scanner) { this.mandatoryList = mandatory; this.optionList = optional; this.goalCredit = credit; this.sc = scanner; } - // Routing: send to function depending on its input - public void route(String input, List> currentSchedules) { - if (currentSchedules != null) { - this.schedules = currentSchedules; - } - - input = input.toLowerCase().trim(); - - if (input.equals("quit")) quit(); - else if (input.equals("edit")) editList(); - else if (input.equals("next")) viewLoop(5); - else { - System.out.println("[Error] Invalid input. Going back to View Mode."); - viewLoop(0); - } - } - - // 'next': loop to show 5 more schedules - private void viewLoop(int startIndex) { - int index = startIndex; - String nextInput; - - while (true) { - ResultView rv = new ResultView(); - nextInput = rv.printBatchAndGetInput(this.schedules, index, this.sc); - - if (nextInput.equals("next")) { - if (index + 5 < schedules.size()) { - index += 5; - } else { - System.out.println("[Info] No more schedules to show."); - } - continue; - } - break; - } + // [핵심] App.java에서 호출하는 메서드 + public void runEditMode() { + System.out.println("\n=========================================="); + System.out.println(" [ EDIT MODE ]"); + System.out.println("=========================================="); - route(nextInput, this.schedules); - } - - // 'edit': edit lists and send it back to InputHandler - private void editList() { - System.out.println("[Edit Mode]"); modifyCourse(this.mandatoryList, this.optionList); - System.out.println("Re-calculating schedules..."); - - Scheduler scheduler = new Scheduler(); - this.schedules = scheduler.schedule(this.mandatoryList, this.optionList, this.goalCredit); + System.out.println("Exiting Edit Mode... Saving changes..."); } - // helper of 'edit' private void modifyCourse(List mandatory, List optional) { - - InputHandler ih = new InputHandler(); + + // [중요] 여기에 'new InputHandler()'가 있으면 절대 안 됩니다! (삭제됨) while (true) { System.out.println("\n------------------------------------------"); @@ -88,39 +41,28 @@ private void modifyCourse(List mandatory, List optional) { System.out.println("2. Add/Edit OPTIONAL Courses"); System.out.println("3. Remove a Course"); System.out.println("4. Change Goal Credit"); - System.out.println("0. Finish Editing (Run Scheduler)"); + System.out.println("0. Finish Editing & View Timetables"); System.out.print("> Select: "); String choice = sc.nextLine().trim(); - // Add if (choice.equals("1")) { System.out.println("\n[Add to Mandatory] Type 'done' to finish."); - ih.inputLoop(sc, mandatory); + // InputHandler 대신 직접 만든 입력 메서드를 씁니다. + addCourseLoop(mandatory, true); } else if (choice.equals("2")) { System.out.println("\n[Add to Optional] Type 'done' to finish."); - ih.inputLoop(sc, optional); + addCourseLoop(optional, false); } else if (choice.equals("3")) { removeCourseHelper(mandatory, optional); } else if (choice.equals("4")) { - System.out.print("Enter new max credit: "); - try { - int newCredit = Integer.parseInt(sc.nextLine().trim()); - if (newCredit > 0) { - this.goalCredit = newCredit; - System.out.println("Goal credit updated."); - } else { - System.out.println("Credit must be positive."); - } - } catch (Exception e) { - System.out.println("Invalid number."); - } + changeCredit(); } else if (choice.equals("0")) { - break; + break; } else { System.out.println("Invalid choice."); @@ -128,7 +70,94 @@ else if (choice.equals("0")) { } } - // helper of 'edit': remove + // [해결책] InputHandler의 입력 로직을 여기로 가져와서 직접 처리합니다. + // 이렇게 하면 '최대 학점'을 묻는 불필요한 질문 없이 과목만 추가할 수 있습니다. + private void addCourseLoop(List targetList, boolean isMandatory) { + System.out.println("------------------------------------------------------------------"); + System.out.println("Format: Name / Credit / Time"); + System.out.println("Example: Data Structure / 3 / Mon 12:30 14:00"); + System.out.println("------------------------------------------------------------------"); + + while (true) { + System.out.print("\nInput (or 'done'): "); + String input = sc.nextLine().trim(); + + if (input.equalsIgnoreCase("done")) break; + + try { + // 1. '/' 기준으로 나누기 + String[] parts = input.split("/"); + if (parts.length != 3) throw new Exception("Use '/' to separate fields"); + + String name = parts[0].trim(); + int credit = Integer.parseInt(parts[1].trim()); + String timeString = parts[2].trim(); + + // 2. 시간 파싱 (Day Start End) + String[] timeParts = timeString.split(" "); + if (timeParts.length != 3) throw new Exception("Time format: Day Start End"); + + int day = parseDay(timeParts[0]); + int startMin = parseMin(timeParts[1]); + int endMin = parseMin(timeParts[2]); + + if (startMin >= endMin) throw new Exception("End time must be after start time"); + + String timeRaw = timeParts[0] + " " + timeParts[1] + "-" + timeParts[2]; + + // 3. 과목 생성 및 추가 + Course c = new Course(name, credit, day, startMin, endMin, timeRaw); + + // 스크린샷에 있던 에러 해결: setMandatory 메서드 호출 + if (isMandatory) c.setMandatory(true); + + targetList.add(c); + System.out.println(" -> [Added] " + name); + + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + System.out.println("Try again: Name / Credit / Day Start End"); + } + } + } + + // [헬퍼] 요일 파싱 + private int parseDay(String d) throws Exception { + d = d.toLowerCase(); + if (d.startsWith("m")) return 0; + if (d.startsWith("tu")) return 1; + if (d.startsWith("w")) return 2; + if (d.startsWith("th")) return 3; + if (d.startsWith("f")) return 4; + throw new Exception("Invalid day"); + } + + // [헬퍼] 시간 파싱 + private int parseMin(String t) throws Exception { + String[] hhmm = t.split(":"); + if (hhmm.length != 2) throw new Exception("Time format HH:MM"); + int h = Integer.parseInt(hhmm[0]); + int m = Integer.parseInt(hhmm[1]); + return h * 60 + m; + } + + // 학점 변경 + private void changeCredit() { + System.out.print("Enter new max credit: "); + try { + int newCredit = Integer.parseInt(sc.nextLine().trim()); + if (newCredit > 0) { + this.goalCredit = newCredit; + System.out.println("Goal credit updated."); + } else { + System.out.println("Credit must be positive."); + } + } catch (Exception e) { + System.out.println("Invalid number."); + } + } + + // 과목 삭제 private void removeCourseHelper(List mandatory, List optional) { System.out.println("\n[Remove Course]"); System.out.println("1. From Mandatory"); @@ -145,7 +174,7 @@ private void removeCourseHelper(List mandatory, List optional) { } if (target.isEmpty()) { - System.out.println("This list is empty."); + System.out.println("List is empty."); return; } @@ -153,7 +182,7 @@ private void removeCourseHelper(List mandatory, List optional) { System.out.println("[" + i + "] " + target.get(i).getName()); } - System.out.print("Enter index to remove (or -1 to cancel): "); + System.out.print("Enter index to remove: "); try { int idx = Integer.parseInt(sc.nextLine().trim()); if (idx >= 0 && idx < target.size()) { @@ -167,7 +196,7 @@ private void removeCourseHelper(List mandatory, List optional) { } } - public void quit(){ - System.out.println("[Bye] Closing system..."); + public int getGoalCredit() { + return this.goalCredit; } } \ No newline at end of file