실행코드 ¶
~cpp code import javax.swing.JOptionPane; public class BaseBall { private int correctNumber; public static void main(String[] args) { BaseBall oneGame = new BaseBall(); oneGame.setCorrectNumber(); int guess = 0, result = 0; do { guess = oneGame.inputGuess(); result = oneGame.compare(guess); if (result != 30) JOptionPane.showMessageDialog(null, (result / 10) + " Strike, " + (result % 10) + " Ball"); }while (result != 30); oneGame.displayResult(); } public void setCorrectNumber() { int setNumber = 0; do { setNumber = (int) (Math.random() * 10); }while (setNumber == 0); correctNumber = setNumber; do { setNumber = (int) (Math.random() * 10); }while (correctNumber == setNumber); correctNumber = correctNumber * 10 + setNumber; do { setNumber = (int) (Math.random() * 10); }while (((int)(correctNumber / 10)) == setNumber || ((int)(correctNumber % 10)) == setNumber); correctNumber = correctNumber * 10 + setNumber; } public int inputGuess() { String input = JOptionPane.showInputDialog("Enter three different number\n"); int guess = Integer.parseInt(input); return guess; } public int compare(int aGuess) { int compareResult = 0, cCorrect = correctNumber; int firstGuess = (int) (aGuess / 100); int secondGuess = ((int) (aGuess / 10)) % 10; int thirdGuess = aGuess % 10; if (((int)(cCorrect / 100) == firstGuess)) compareResult ++; cCorrect = cCorrect % 100; if (((int)(cCorrect / 10) == secondGuess)) compareResult ++; cCorrect = cCorrect % 10; if (cCorrect == thirdGuess) compareResult ++; compareResult *= 10; cCorrect = correctNumber; if (((int)(cCorrect / 100)) == secondGuess || ((int)(cCorrect / (100))) == thirdGuess) compareResult ++; cCorrect = cCorrect % 100; if (((int)(cCorrect / 10)) == firstGuess || ((int)(cCorrect / (10))) == thirdGuess) compareResult ++; cCorrect = cCorrect % 10; if ((cCorrect == firstGuess) || (cCorrect == secondGuess)) compareResult ++; return compareResult; } public void displayResult() { JOptionPane.showMessageDialog(null, "You are right!\n Answer is " + correctNumber); } }
개선 시도 ¶
집에서 놀다가 우연히 여기를 와서 고쳐봅니다. 조금 더 생각해 보시면 되지요. 저에게 재미있는 경험을 주었는데, 문원명 후배님도 보시라고 과정을 만들어 두었습니다. 선행학습으로 JUnit이 있어야 하는데, http://junit.org 에서 궁금하시면 관련문서를 보시고 선배들에게 물어보세요.
용기가 내셔서 저를 찾으면...
--NeoCoin
--NeoCoin
- 처음에는 Test-Driven Development 에 입각하여 만들어 보려고 했으나, Java를 거의 처음 시작하고 프로그래밍 경험의 공백기간이 길었던게 큰 타격이었습니다ㅠㅠ. 결국에는 문법과 알고리즘에만 신경을 쓰다보니 TDD방식으로 다루기가 쉽지 않네요. 개선 조언을 해 주신 류상민 선배님 감사합니다 ㅎㅎ -문원명
과정 ¶
초기의 테스트 가능하도록 원하는 값으로 무작위로 나오는 답안을 세팅하는 함수 추가.
setCorrectNumber 하기도 귀찮아서 compare 부분을 확장 , 내부에서 쓰일일이 없는 함수로 제거 목적으로 코스 수정
readability 를 위해 필요없는 typecasting 문법들 제거 (Java Language Specification의 규칙들을 보세요. 해당 typecasting은 거의다 필요 없는겁니다.) 유의미한 단위로 분리
해당 함수를 알고리즘에 적용, Test는 계속 녹색 .
결과 값을 의미를 드러낼수 있도록 변경
더이상 함수가 this.correctNumber에 의존할 필요 없으므로 코드 개선
숫자 패턴을 파악하고 중복 개선, magic number 제거
두개의 루프의 정확한 의미 파악, 합침
해당 루틴이 지날때 마다 result 값에 10과 1을 더해주는 방식으로 바꿈
기타 overloading된 무의미한 compare를 정리. 소스상 setCorrectNumber를 생성자로 옮김.
더 개선하고 싶으나 그냥 하기 지쳐서 여기서 그만..
~java // ..생략 void setCorrectNumber(int in) { correctNumber = in; } @Test public void test2() { BaseBall oneGame = new BaseBall(); oneGame.setCorrectNumber(123); assertEquals(30, oneGame.compare(123)); assertEquals(10, oneGame.compare(100, 123)); assertEquals(2, oneGame.compare(310, 123)); } //..생략
~java @Test public void test2() { BaseBall oneGame = new BaseBall(); assertEquals(30, oneGame.compare(123, 123)); assertEquals(10, oneGame.compare(100, 123)); assertEquals(2, oneGame.compare(310, 123)); } public int compare(int aGuess) { return compare(aGuess, correctNumber); } public int compare(int aGuess, int answer) { correctNumber = answer; int compareResult = 0, cCorrect = correctNumber; //생략 }
~java public int compare(int aGuess, int answer) { correctNumber = answer; int compareResult = 0, cCorrect = correctNumber; int firstGuess = aGuess / 100 % 10; int secondGuess = aGuess / 10 % 10; int thirdGuess = aGuess % 10; if (cCorrect / 100 == firstGuess) compareResult++; cCorrect = cCorrect % 100; if (cCorrect / 10 == secondGuess) compareResult++; cCorrect = cCorrect % 10; if (cCorrect == thirdGuess) compareResult++; compareResult *= 10; cCorrect = correctNumber; if ((cCorrect / 100) == secondGuess || (cCorrect / 100) == thirdGuess) compareResult++; cCorrect = cCorrect % 100; if ((cCorrect / 10) == firstGuess || (cCorrect / 10) == thirdGuess) compareResult++; cCorrect = cCorrect % 10; if ((cCorrect == firstGuess) || (cCorrect == secondGuess)) compareResult++; return compareResult; }특정 포지션의 숫자를 얻어오기 위한 반복 부분들 발견, 특정 포지션을 얻어오는 함수 만들기
~java int positionNum(int value, int pos) { if (pos < 1) throw new IndexOutOfBoundsException("pos값은 1이상을 넣어주세요."); int a = value % (int) Math.pow(10, pos); return a / (int) Math.pow(10, pos - 1); } @Test public void getPositionNum() { assertEquals(3, positionNum(123, 1)); assertEquals(2, positionNum(123, 2)); assertEquals(1, positionNum(123, 3)); }
결과 값을 의미를 드러낼수 있도록 변경
더이상 함수가 this.correctNumber에 의존할 필요 없으므로 코드 개선
~java public int compare(int aGuess, int cCorrect) { int firstGuess = positionNum(aGuess,3); int secondGuess = positionNum(aGuess,2); int thirdGuess = positionNum(aGuess,1); int strike=0; if (positionNum(cCorrect,3) == firstGuess) strike++; if (positionNum(cCorrect,2) == secondGuess) strike++; if (positionNum(cCorrect,1) == thirdGuess) strike++; int ball=0; if (positionNum(cCorrect,3) == secondGuess || positionNum(cCorrect,3) == thirdGuess) ball++; if (positionNum(cCorrect,2) == firstGuess || positionNum(cCorrect,2) == thirdGuess) ball++; if ((positionNum(cCorrect,1) == firstGuess) || (positionNum(cCorrect,1) == secondGuess)) ball++; return ball + strike*10; }코드에 약간 패턴이 보인다. inline시키면 보일것 같다. 그래서 xxxGuess 를 inline
~java public int compare(int aGuess, int answer) { int cCorrect = answer; int strike=0; if (positionNum(cCorrect,3) == positionNum(aGuess,3)) strike++; if (positionNum(cCorrect,2) == positionNum(aGuess,2)) strike++; if (positionNum(cCorrect,1) == positionNum(aGuess,1)) strike++; int ball=0; if (positionNum(cCorrect,3) == positionNum(aGuess,2) || positionNum(cCorrect,3) == positionNum(aGuess,1)) ball++; if (positionNum(cCorrect,2) == positionNum(aGuess,3) || positionNum(cCorrect,2) == positionNum(aGuess,1)) ball++; if ((positionNum(cCorrect,1) == positionNum(aGuess,3)) || (positionNum(cCorrect,1) == positionNum(aGuess,2))) ball++; return ball + strike*10; }
~java public int compare(int aGuess, int cCorrect) { final int START_POS = 1,END_POS = 3; int strike = 0; for (int pos = END_POS; pos >=START_POS; pos--) { if (positionNum(cCorrect, pos) == positionNum(aGuess, pos)) strike++; } int ball = 0; for (int corPos = END_POS; corPos >=START_POS; corPos--) { for (int guePos = END_POS; guePos > 0; guePos--) { if (corPos != guePos && positionNum(cCorrect, corPos) == positionNum(aGuess,guePos)) ball++; } } return ball + strike * 10; }
~java public int compare(int aGuess, int cCorrect) { final int START_POS = 1, END_POS = 3; int strike = 0, ball = 0; for (int corPos = END_POS; corPos >= START_POS; corPos--) { for (int guePos = END_POS; guePos >= START_POS; guePos--) { if (positionNum(cCorrect, corPos) == positionNum(aGuess, guePos)) { if (corPos == guePos) strike++; else ball++; } } } return ball + strike * 10; }
~java public int compare(int aGuess, int cCorrect) { final int START_POS = 1, END_POS = 3, STRIKE_DELTA = 10, BALL_DELTA = 1; int result = 0; //10자리가 strike, 1자리고 ball 값 for (int corPos = END_POS; corPos >= START_POS; corPos--) for (int guePos = END_POS; guePos >= START_POS; guePos--) if (positionNum(cCorrect, corPos) == positionNum(aGuess, guePos)) result += (corPos == guePos) ? STRIKE_DELTA : BALL_DELTA; return result; }
더 개선하고 싶으나 그냥 하기 지쳐서 여기서 그만..
~java package test; import static org.junit.Assert.assertEquals; import org.junit.Test; import javax.swing.JOptionPane; public class BaseBall { private int correctNumber; public static void main(String[] args) { final int END_GAME_NUM = 30; BaseBall oneGame = new BaseBall(); while (true) { int result = oneGame.runOneTurn(); if (result == END_GAME_NUM) break; else JOptionPane.showMessageDialog(null, String.format("%d Strike, %d Ball", result / 10, result % 10)); } oneGame.displayResult(); } public int runOneTurn() { return compare(this.correctNumber, inputGuess()); } public BaseBall() { int setNumber = 0; do { setNumber = (int) (Math.random() * 10); } while (setNumber == 0); correctNumber = setNumber; do { setNumber = (int) (Math.random() * 10); } while (correctNumber == setNumber); correctNumber = correctNumber * 10 + setNumber; do { setNumber = (int) (Math.random() * 10); } while (correctNumber / 10 == setNumber || correctNumber % 10 == setNumber); correctNumber = correctNumber * 10 + setNumber; } public int inputGuess() { String input = JOptionPane .showInputDialog("Enter three different number\n"); int guess = Integer.parseInt(input); return guess; } int positionNum(int value, int pos) { if (pos < 1) throw new IndexOutOfBoundsException("pos값은 1이상"); int a = value % (int) Math.pow(10, pos); return a / (int) Math.pow(10, pos - 1); } public int compare(int aGuess, int cCorrect) { final int START_POS = 1, END_POS = 3, STRIKE_DELTA = 10, BALL_DELTA = 1; int result = 0; // 10자리가 strike, 1자리가 ball 값 for (int corPos = END_POS; corPos >= START_POS; corPos--) for (int guePos = END_POS; guePos >= START_POS; guePos--) if (positionNum(cCorrect, corPos) == positionNum(aGuess, guePos)) result += (corPos == guePos) ? STRIKE_DELTA : BALL_DELTA; return result; } public void displayResult() { JOptionPane.showMessageDialog(null, "You are right!\n Answer is " + correctNumber); } @Test public void getPositionNum() { assertEquals(3, positionNum(123, 1)); assertEquals(2, positionNum(123, 2)); assertEquals(1, positionNum(123, 3)); } @Test public void test2() { BaseBall oneGame = new BaseBall(); assertEquals(30, oneGame.compare(123, 123)); assertEquals(10, oneGame.compare(100, 123)); assertEquals(2, oneGame.compare(310, 123)); } }