Giter Site home page Giter Site logo

penekhun / baekjoon-java-starter Goto Github PK

View Code? Open in Web Editor NEW
26.0 2.0 4.0 2.33 MB

자바를 이용하여 백준 문제를 풀 때 프로젝트 생성 및 주어진 예시 입출력에 대한 자동 검증을 도와주는 프로그램입니다. 여러분은 이제 문제 풀이에만 집중하세요.

License: MIT License

Java 95.78% Shell 4.22%
baekjoon baekjoon-online-judge baekjoon-solution problem-solving

baekjoon-java-starter's Introduction

Baekjoon-java-starter

해당 프로젝트는 자바를 이용하여 백준 문제를 풀때, 프로젝트 생성 및 테스트 케이스 자동 생성을 지원합니다.
"여러분은 단지 문제 풀이에만 집중하면 됩니다."

매번 귀찮게 프로젝트를 만들지 마세요 그냥 번호를 입력하세요
매번 귀찮게 복붙하지 마세요 그냥 실행만 하세요

어떻게 쓰면 되나요?

  1. 프로그램 실행
  2. 문제 번호 입력: 시작하기 위해 문제 번호를 입력하세요.
  3. 자동 설정: 입력한 번호로 디렉터리가 생성되며, Main.java와 TestHelper.java 파일이 자동으로 준비됩니다. 이후 인텔리제이가 실행됩니다.
  4. 코딩 및 테스트: Main.java에서 알고리즘을 구현하고, TestHelper.java를 실행하여 코드를 테스트하세요. 모든 테스트 케이스가 자동으로 실행됩니다.
  5. 제출: 문제 해결이 완료되면, Main.java의 내용을 백준에 제출합니다.

요구 환경

  • 인터넷 연결
    백준 사이트에서 문제를 파싱하기 위해 인터넷 연결이 필요합니다.
  • JAVA 13 버전 이상
  • 인텔리제이

설치 및 사용 방법

  1. 최신 릴리즈 에서 Baekjoon-java-starter.zip 파일을 다운로드합니다.

  2. 압축을 풀고, 아래 명령어를 실행합니다.

    java -jar Baekjoon-java-starter.jar
  3. 이제 백준 문제 번호를 입력하면 됩니다.

기본으로 생성되는 Main.java 템플릿을 변경하고 싶다면, 코드 템플릿 변경하기을 참조해주세요.
그 외 자세한 내용은 사용 가이드를 참고해주세요.

기여하기

이 프로젝트에 대한 이슈와 풀 리퀘스트는 언제나 환영합니다. 기여 가이드라인을 확인해 주세요. 감사합니다!

라이센스

이 프로젝트는 MIT 라이센스를 따릅니다.

MIT Licence

baekjoon-java-starter's People

Contributors

penekhun avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

baekjoon-java-starter's Issues

리팩터링

현재 프로덕션 코드가 유닛테스트에 취약하게 작성이 되어있는데,
적당히 추상화 + 의존성 주입을 사용해서 코드간 결합을 느슨하게 변경하고
Fake객체를 만들어 누락된 테스트를 작성한다.

[버그 신고] 독립적이지 않는 테스트 실행

버그 신고

Main.java에서 static 변수를 초기화해주지 않으면, 각 테스트가 독립적으로 실행되지 않는 버그가 있습니다.

버그 설명 및 재연 방법

Reproduce of Main.java

public class Main {
  static int n;
  static int m;
  static int[] arr;
  static StringBuilder result = new StringBuilder();

  public static int[] appendArr(int[] arr, int item) {
    int[] result = Arrays.copyOf(arr, arr.length + 1);
    result[arr.length] = item;
    return result;
  }

  public static void solution(int[] selected){
    if (selected.length == m) {
      result.append(Arrays.stream(selected).mapToObj(Integer::toString).reduce((a, b) -> a + " " + b).get())
          .append("\n");
      return;
    }

    for (int i = 1; i <= n; i++) {
      int finalI = i;
      if (Arrays.stream(selected).noneMatch(s -> s == finalI)) {
        solution(appendArr(selected, i));
      }
    }
  }

  public static void main(String[] args) throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    String[] input = br.readLine().split(" ");
    n = Integer.parseInt(input[0]); // 최대 수
    m = Integer.parseInt(input[1]); // m개 뽑기
    arr = new int[m];

    for (int i = 1; i <= n; i++) {
      solution(new int[]{i});
    }
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    bw.write(result.toString());
    bw.flush();
  }
}

여기서 전역 static 변수 result 에서 매 테스트마다 값이 누적되면서, 각 테스트들이 독립적으로 실행되지 않음.

[버그 신고] TestHelper에서 값이 정확하나 케이스가 실패되는 경우

버그 신고

사용 환경

Java 11, Mac OS Sonoma 14.4.1, 인텔리제이 2023.3.2

버그 설명 및 재현 방법

스크린샷 2024-04-09 11 13 06

특정 문제에서 테스트 케이스가 패스되지 않는 경우 발견하여 이슈 올려드립니다.

TestHelper에 정해져 있는 output의 경우 Text Block 안에서 "케이스의 정답\n"의 꼴로 저장되나,
main()에서 출력되는 값이 정확하게 "케이스의 정답\n"이 아닌 경우 틀린 케이스로 출력됩니다.

-> 이 부분은 백준에서도 허용해주는 부분으로, 위와 같은 케이스에서 백준 제출 시 답이 정확하다면 AC를 받습니다.

[버그 신고] Main.java의 final 전역변수에 의한 오류메세지

버그 신고

버그 설명 및 재현 방법

Error Message

userspenekhunLibraryJavaJavavintualMachinesopenjdk-21 0

Reproduce

  • main.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/*
  BAEKJOON 20529 가장 가까운 세 사람의 심리적 거리
  https://www.acmicpc.net/problem/20529
*/

public class Main {

  public static final String[] MBTI_ARRAY = new String[] {
      "ISTJ", "ISFJ", "INFJ", "INTJ", "ISTP", "ISFP", "INFP", "INTP", "ESTP", "ESFP", "ENFP", "ENTP", "ESTJ",
      "ESFJ", "ENFJ", "ENTJ"
  };

  public static int getDistance(String a, String b, String c) {
    int distance = getDistance(a, b);
    distance += getDistance(b, c);
    distance += getDistance(a, c);

    return distance;
  }

  private static int getDistance(String a, String b) {
    int distance = 0;

    if (a.charAt(0) != b.charAt(0)) {
      distance += 1;
    }

    if (a.charAt(1) != b.charAt(1)) {
      distance += 1;
    }

    if (a.charAt(2) != b.charAt(2)) {
      distance += 1;
    }

    if (a.charAt(3) != b.charAt(3)) {
      distance += 1;
    }
    return distance;
  }

  public static void main(String[] args) throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    int tCase = Integer.parseInt(br.readLine());
    StringBuilder result = new StringBuilder();

    for (int t = 0; t < tCase; t++) {
      int n = Integer.parseInt(br.readLine());
      String[] students = br.readLine().split(" ");

      int score = Integer.MAX_VALUE;
      if (n >= 33) {
        System.out.println(0);
        continue;
      }

      for (int i = 0; i < n; i++) {
        for (int k = i + 1; k < n; k++) {
          for (int j = k + 1; j < n; j++) {
            score = Math.min(score, getDistance(students[i], students[k], students[j]));
          }
        }
      }

      System.out.println(score);
    }
  }
}

To Fix it

final 키워드가 있으면 변수값 초기화를 진행하지 않도록 TestHelper.java를 수정해야함.

성능 지표 생성 기능 제안

TestHelper 실행해서 케이스가 성공하면,
TestResult.txt를 생성해서 실행된 코드들의 성능 지표 (ex. 메모리 등)를 기록하는 기능이 있으면 좋을 것 같습니다.

채점 오류

Main

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/*
  BAEKJOON 15686 치킨 배달
  https://www.acmicpc.net/problem/15686
*/

public class Main {
  static final int EMPTY = 0;
  static final int HOUSE = 1;
  static final int CHICKEN = 2;
  static int[][] map;
  static int n;
  static int m;
  static int result = Integer.MAX_VALUE;
  static List<Position> chickenPos = new ArrayList<>();
  static List<Position> housePos = new ArrayList<>();

  static class Position {
    public int x;
    public int y;

    public Position(int x, int y) {
      this.x = x;
      this.y = y;
    }
  }

  static void calculateChickenDistance(List<Position> selectedChickens) {
    int cityChickenSum = 0;
    for (Position house : housePos) {
      int houseChickenMin = Integer.MAX_VALUE;
      for (Position chicken : selectedChickens) {
        houseChickenMin = Math.min(houseChickenMin, Math.abs(house.x - chicken.x) + Math.abs(house.y - chicken.y));
      }
      cityChickenSum += houseChickenMin;
    }
    result = Math.min(result, cityChickenSum);
  }

  static void generateCombinations(int start, List<Position> selectedChickens) {
    if (selectedChickens.size() == m) {
      calculateChickenDistance(selectedChickens);
      return;
    }

    for (int i = start; i < chickenPos.size(); i++) {
      selectedChickens.add(chickenPos.get(i));
      generateCombinations(i + 1, selectedChickens);
      selectedChickens.remove(selectedChickens.size() - 1);
    }
  }

  public static void main(String[] args) throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    String[] NM = br.readLine().split(" ");
    n = Integer.parseInt(NM[0]);
    m = Integer.parseInt(NM[1]);
    map = new int[n][n];
    for (int i = 0; i < n; i++) {
      int[] line = Arrays.stream(br.readLine().split(" ")).mapToInt(Integer::parseInt).toArray();
      for (int k = 0; k < line.length; k++) {
        map[i][k] = line[k];
        if (line[k] == CHICKEN) {
          chickenPos.add(new Position(k, i));
        } else if (line[k] == HOUSE) {
          housePos.add(new Position(k, i));
        }
      }
    }

    generateCombinations(0, new ArrayList<>());
    System.out.println(result);
  }
}

Helper

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 이 테스트 코드는 <a href="https://github.com/PENEKhun/Baekjoon-java-starter">Baekjoon-java-starter</a>를 사용하여
 * 생성되었습니다.
 *
 * @author PENEKhun
 */
public class TestHelper {

  private static final Map<Field, byte[]> initialStates = new HashMap<>();
  private static final int timeLimit = 2;

  public static void main(String[] args) {
    captureInitialState();

    TestCase[] testCases = new TestCase[] {
        new TestCase(
// input
"""
5 3
0 0 1 0 0
0 0 2 0 1
0 1 2 0 0
0 0 1 0 0
0 0 0 0 2
""",
// output
"""
5
"""),
new TestCase(
// input
"""
5 2
0 2 0 1 0
1 0 1 0 0
0 0 0 0 0
2 0 0 1 1
2 2 0 1 2
""",
// output
"""
10
"""),
new TestCase(
// input
"""
5 1
1 2 0 0 0
1 2 0 0 0
1 2 0 0 0
1 2 0 0 0
1 2 0 0 0
""",
// output
"""
11
"""),
new TestCase(
// input
"""
5 1
1 2 0 2 1
1 2 0 2 1
1 2 0 2 1
1 2 0 2 1
1 2 0 2 1
""",
// output
"""
32
"""),

    };

    int passedCases = runTestCases(testCases);

    printSummary(passedCases, testCases.length);
  }

  private static int runTestCases(TestCase[] testCases) {
    int passedCases = 0;

    for (int i = 0; i < testCases.length; i++) {
      resetToInitialState();
      if (runSingleTestCase(testCases[i], i + 1)) {
        passedCases++;
      }
    }

    return passedCases;
  }

  private static boolean runSingleTestCase(TestCase testCase, int caseNumber) {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    PrintStream originalOut = System.out;
    setupStreams(testCase.input, outputStream);

    ExecutorService executor = Executors.newSingleThreadExecutor();
    Future<?> future = submitTask(executor);

    boolean testCaseException = false;
    try {
      future.get(timeLimit, TimeUnit.SECONDS);
    } catch (TimeoutException e) {
      handleException(originalOut, caseNumber, testCase, "시간 초과 발생", e);
      testCaseException = true;
    } catch (InterruptedException | ExecutionException e) {
      handleException(originalOut, caseNumber, testCase, "Main() Exception 발생", e);
      testCaseException = true;
    } finally {
      executor.shutdown();
    }

    if (!testCaseException) {
      return compareOutput(testCase, caseNumber, outputStream.toString(), originalOut);
    }

    return false;
  }

  private static Future<?> submitTask(ExecutorService executor) {
    return executor.submit(() -> {
      try {
        Main.main(new String[0]);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    });
  }

  private static void setupStreams(String input, ByteArrayOutputStream outputStream) {
    System.setOut(new PrintStream(outputStream));
    System.setIn(new ByteArrayInputStream(input.getBytes()));
  }

  private static boolean compareOutput(TestCase testCase, int caseNumber, String output, PrintStream originalOut) {
    System.setOut(originalOut);
    String actualOutput = removeTrailingSpaces(output);
    String expectedOutput = testCase.expectedOutput.stripTrailing();

    if (actualOutput.equals(expectedOutput)) {
      return true;
    } else {
      printFail(caseNumber, testCase, red("[실제 값]\n%s\n\n출력 값이 기대한 값과 다릅니다.").formatted(actualOutput));
      return false;
    }
  }

  private static String removeTrailingSpaces(String input) {
    String[] lines = input.split("\n");
    StringBuilder result = new StringBuilder();

    for (String line : lines) {
      result.append(line.stripTrailing()).append("\n");
    }

    if (!result.isEmpty()) {
      result.setLength(result.length() - 1);
    }

    return result.toString();
  }

  private static void handleException(PrintStream originalOut, int caseNumber, TestCase testCase, String message, Exception e) {
    System.setOut(originalOut);
    printFail(caseNumber, testCase, message);
    e.printStackTrace(originalOut);
  }

  private static void captureInitialState() {
    try {
      Field[] fields = Main.class.getDeclaredFields();
      for (Field field : fields) {
        if (Modifier.isStatic(field.getModifiers())) {
          field.setAccessible(true);
          Object fieldValue = field.get(null);
          byte[] serializedField = SerializationUtils.serialize(fieldValue);
          initialStates.put(field, serializedField);
        }
      }
    } catch (Exception e) {
      System.out.println(red("Main 클래스에 접근할 수 없습니다."));
    }
  }

  private static void resetToInitialState() {
    try {
      for (Map.Entry<Field, byte[]> entry : initialStates.entrySet()) {
        Field field = entry.getKey();
        Object originalState = SerializationUtils.deserialize(entry.getValue());
        field.setAccessible(true);
        field.set(null, originalState);
      }
    } catch (Exception e) {
      System.out.println(red("Main 클래스에 접근할 수 없습니다."));
    }
  }

  private static void printFail(int caseNumber, TestCase testCase, String message) {
    System.out.printf("""
            ====== %s ======
            [입력 값]
            %s
            [기대 값]
            %s
            """, red(caseNumber + " 번째 케이스 실패"), testCase.input, testCase.expectedOutput);
    System.out.println(green(message));
  }

  private static void printSummary(int passedCases, int totalCases) {
    System.out.println("===============");
    System.out.println("테스트 완료 (" + passedCases + " / " + totalCases + ")");
    if (passedCases == totalCases) {
      System.out.println("주어진 케이스에 대해 잘 동작하고 있습니다.");
    }
  }

  private static String red(String message) {
    return String.format("\u001B[31m%s\u001B[0m", message);
  }

  private static String green(String message) {
    return String.format("\u001B[32m%s\u001B[0m", message);
  }

  private static class TestCase {
    public final String input;
    public final String expectedOutput;

    public TestCase(String input, String expectedOutput) {
      this.input = input;
      this.expectedOutput = expectedOutput;
    }
  }

  public static class SerializationUtils {
    public static byte[] serialize(Object obj) throws IOException {
      try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
          ObjectOutputStream oos = new ObjectOutputStream(bos)) {
        oos.writeObject(obj);
        return bos.toByteArray();
      }
    }

    public static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
      try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
          ObjectInputStream ois = new ObjectInputStream(bis)) {
        return ois.readObject();
      }
    }
  }
}

Result

/Users/penekhun/Library/Java/JavaVirtualMachines/openjdk-21.0.1/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=51631:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath /Volumes/BackupDrive/JavaAlgorithm/BOJ/p15686/out/production/p15686 TestHelper
Main 클래스에 접근할 수 없습니다.
Main 클래스에 접근할 수 없습니다.
====== 2 번째 케이스 실패 ======
[입력 값]
5 2
0 2 0 1 0
1 0 1 0 0
0 0 0 0 0
2 0 0 1 1
2 2 0 1 2

[기대 값]
10

[실제 값]
5

출력 값이 기대한 값과 다릅니다.
Main 클래스에 접근할 수 없습니다.
====== 3 번째 케이스 실패 ======
[입력 값]
5 1
1 2 0 0 0
1 2 0 0 0
1 2 0 0 0
1 2 0 0 0
1 2 0 0 0

[기대 값]
11

[실제 값]
5

출력 값이 기대한 값과 다릅니다.
Main 클래스에 접근할 수 없습니다.
====== 4 번째 케이스 실패 ======
[입력 값]
5 1
1 2 0 2 1
1 2 0 2 1
1 2 0 2 1
1 2 0 2 1
1 2 0 2 1

[기대 값]
32

[실제 값]
5

출력 값이 기대한 값과 다릅니다.
===============
테스트 완료 (1 / 4)

종료 코드 0(으)로 완료된 프로세스

But

하지만 맞았죠
image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.