From a15c29babf050d56ba644952beb8363f357534ee Mon Sep 17 00:00:00 2001 From: devny Date: Fri, 28 Feb 2025 15:42:53 +0900 Subject: [PATCH 01/39] =?UTF-8?q?2025=2002=2028=20by=20devny=20-=20initial?= =?UTF-8?q?=20commit=20-=20=EB=A7=A4=EC=A7=81=20=EB=84=98=EB=B2=84,=20?= =?UTF-8?q?=EB=A7=A4=EC=A7=81=20=EC=8A=A4=ED=8A=B8=EB=A7=81(=EC=83=81?= =?UTF-8?q?=EC=88=98=EB=A1=9C=20=EC=B6=94=EC=B6=9C=EB=90=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EC=9D=80=20=EC=9C=A0=EC=9D=98=EB=AF=B8=ED=95=9C=20?= =?UTF-8?q?=EA=B0=92)=EC=9D=84=20=EC=83=81=EC=88=98=EB=A1=9C=20=EC=B6=94?= =?UTF-8?q?=EC=B6=9C=ED=95=98=EC=97=AC=20=EC=9C=A0=EC=A7=80=EB=B3=B4?= =?UTF-8?q?=EC=88=98=EC=99=80=20=EA=B0=80=EB=8F=85=EC=84=B1=EC=9D=84=20?= =?UTF-8?q?=EB=86=92=EC=9E=84=20nyahn=20A=20minute=20ago?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 9 + .../minesweeper/tobe/MinesweeperGame.java | 328 +++++++++++------- 2 files changed, 207 insertions(+), 130 deletions(-) diff --git a/.gitignore b/.gitignore index 63d2f1a0c..5c247a149 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .gradle build/ +build !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ @@ -34,3 +35,11 @@ out/ ### VS Code ### .vscode/ +### Example user template template +### Example user template + +# IntelliJ project files +.idea +*.iml +out +gen diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index dd85c3ce0..783ad92b0 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -5,175 +5,243 @@ public class MinesweeperGame { - private static String[][] board = new String[8][10]; - private static Integer[][] landMineCounts = new Integer[8][10]; - private static boolean[][] landMines = new boolean[8][10]; + // 중요한 문자열, 숫자야. 유지보수할때 잘 봐야해! 할 수 있는 것 = 매직넘버, 매직스트링 + public static final int BOARD_ROW_SIZE = 8; + public static final int BOARD_COL_SIZE = 10; + // 상수 컨벤션 = 대문자와 언더스코어로 이루어져 있게 해야함 + private static final String[][] BOARD = new String[BOARD_ROW_SIZE][BOARD_COL_SIZE]; + private static final Integer[][] NEAR_BY_LAND_MINE_COUNTS = new Integer[BOARD_ROW_SIZE][BOARD_COL_SIZE]; + private static final boolean[][] LAND_MINES = new boolean[BOARD_ROW_SIZE][BOARD_COL_SIZE]; + public static final int LAND_MINES_COUNT = 10; + public static final String FLAG_SIGN = "⚑"; + public static final String LAND_MINE_SIGN = "☼"; + public static final String CLOSED_CELL_SIGN = "□"; + public static final String OPENED_CELL_SIGN = "■"; + private static int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 public static void main(String[] args) { - System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); - System.out.println("지뢰찾기 게임 시작!"); - System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + showGameStartCommand(); Scanner scanner = new Scanner(System.in); - for (int i = 0; i < 8; i++) { - for (int j = 0; j < 10; j++) { - board[i][j] = "□"; + initializeGame(); + while (true) { + showBoard(); + if (doesUserWinTheGame()) { + System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); + break; + } + if (doesUserLoseTheGame()) { + System.out.println("지뢰를 밟았습니다. GAME OVER!"); + break; + } + System.out.println(); + String cellInput = getCellInputFromUser(scanner); + String userActionInput = getUserActionInputFromUser(scanner); + + int selectedColIndex = getSelectedColIndex(cellInput); + int selectedRowIndex = getSelectedRowIndex(cellInput); + + if (doesUserChooseToPlantFlag(userActionInput)) { + BOARD[selectedRowIndex][selectedColIndex] = FLAG_SIGN; + checkIfGameIsOver(); + } else if (doesUserChooseToOpenCell(userActionInput)) { + if (isLandMineCell(selectedRowIndex, selectedColIndex)) { + BOARD[selectedRowIndex][selectedColIndex] = LAND_MINE_SIGN; + changeGameStatusToLose(); + continue; + } else { + open(selectedRowIndex, selectedColIndex); + } + checkIfGameIsOver(); + } else { + System.out.println("잘못된 번호를 선택하셨습니다."); + } + } + } + + private static void changeGameStatusToLose() { + gameStatus = -1; + } + + private static boolean isLandMineCell(int selectedRowIndex, int selectedColIndex) { + return LAND_MINES[selectedRowIndex][selectedColIndex]; + } + + private static boolean doesUserChooseToOpenCell(String userActionInput) { + return userActionInput.equals("1"); + } + + private static boolean doesUserChooseToPlantFlag(String userActionInput) { + return userActionInput.equals("2"); + } + + private static int getSelectedRowIndex(String cellInput) { + char cellInputRow = cellInput.charAt(1); + return convertRowFrom(cellInputRow); + } + + private static int getSelectedColIndex(String cellInput) { + char cellInputCol = cellInput.charAt(0); + return convertColFrom(cellInputCol); + } + + private static String getUserActionInputFromUser(Scanner scanner) { + System.out.println("선택한 셀에 대한 행위를 선택하세요. (1: 오픈, 2: 깃발 꽂기)"); + return scanner.nextLine(); + } + + private static String getCellInputFromUser(Scanner scanner) { + System.out.println("선택할 좌표를 입력하세요. (예: a1)"); + + return scanner.nextLine(); + } + + private static boolean doesUserLoseTheGame() { + return gameStatus == -1; + } + + private static boolean doesUserWinTheGame() { + return gameStatus == 1; + } + + private static void checkIfGameIsOver() { + boolean isAllOpened = isAllCellOpened(); + if (isAllOpened) { // 게임 이긴 것 + changeGameStatusToWin(); + } + } + + private static void changeGameStatusToWin() { + gameStatus = 1; + } + + private static boolean isAllCellOpened() { + boolean isAllOpened = true; + for (int row = 0; row < BOARD_ROW_SIZE; row++) { + for (int col = 0; col < BOARD_COL_SIZE; col++) { + if (BOARD[row][col].equals(CLOSED_CELL_SIGN)) { // 네모 박스가 하나라도 있는지 확인 + isAllOpened = false; + } + } + } + return isAllOpened; + } + + private static int convertRowFrom(char cellInputRow) { + return Character.getNumericValue(cellInputRow) - 1; + } + + private static int convertColFrom(char cellInputCol) { +// int selectedColIndex; + switch (cellInputCol) { + case 'a': + return 0; + case 'b': + return 1; + case 'c': + return 2; + case 'd': + return 3; + case 'e': + return 4; + case 'f': + return 5; + case 'g': + return 6; + case 'h': + return 7; + case 'i': + return 8; + case 'j': + return 9; + default: + return -1; + } +// return selectedColIndex; + } + + private static void showBoard() { + System.out.println(" a b c d e f g h i j"); + for (int row = 0; row < BOARD_ROW_SIZE; row++) { + System.out.printf("%d ", row + 1); + for (int col = 0; col < BOARD_COL_SIZE; col++) { + System.out.print(BOARD[row][col] + " "); + } + System.out.println(); + } + } + + private static void initializeGame() { + for (int row = 0; row < BOARD_ROW_SIZE; row++) { + for (int col = 0; col < BOARD_COL_SIZE; col++) { + BOARD[row][col] = CLOSED_CELL_SIGN; } } - for (int i = 0; i < 10; i++) { - int col = new Random().nextInt(10); - int row = new Random().nextInt(8); - landMines[row][col] = true; + for (int i = 0; i < LAND_MINES_COUNT; i++) { // 10 이 col 숫자가아니라 지뢰숫자 + int col = new Random().nextInt(BOARD_COL_SIZE); + int row = new Random().nextInt(BOARD_ROW_SIZE); + LAND_MINES[row][col] = true; } - for (int i = 0; i < 8; i++) { - for (int j = 0; j < 10; j++) { + for (int row = 0; row < BOARD_ROW_SIZE; row++) { + for (int col = 0; col < BOARD_COL_SIZE; col++) { int count = 0; - if (!landMines[i][j]) { - if (i - 1 >= 0 && j - 1 >= 0 && landMines[i - 1][j - 1]) { + if (!isLandMineCell(row, col)) { + if (row - 1 >= 0 && col - 1 >= 0 && isLandMineCell(row - 1,col - 1)) { count++; } - if (i - 1 >= 0 && landMines[i - 1][j]) { + if (row - 1 >= 0 && isLandMineCell(row - 1, col)) { count++; } - if (i - 1 >= 0 && j + 1 < 10 && landMines[i - 1][j + 1]) { + if (row - 1 >= 0 && col + 1 < BOARD_COL_SIZE && isLandMineCell(row - 1, col + 1)) { count++; } - if (j - 1 >= 0 && landMines[i][j - 1]) { + if (col - 1 >= 0 && isLandMineCell(row, col - 1)) { count++; } - if (j + 1 < 10 && landMines[i][j + 1]) { + if (col + 1 < BOARD_COL_SIZE && isLandMineCell(row, col + 1)) { count++; } - if (i + 1 < 8 && j - 1 >= 0 && landMines[i + 1][j - 1]) { + if (row + 1 < BOARD_ROW_SIZE && col - 1 >= 0 && isLandMineCell(row + 1, col - 1)) { count++; } - if (i + 1 < 8 && landMines[i + 1][j]) { + if (row + 1 < BOARD_ROW_SIZE && isLandMineCell(row + 1, col)) { count++; } - if (i + 1 < 8 && j + 1 < 10 && landMines[i + 1][j + 1]) { + if (row + 1 < BOARD_ROW_SIZE && col + 1 < BOARD_COL_SIZE && isLandMineCell(row + 1, col + 1)) { count++; } - landMineCounts[i][j] = count; + NEAR_BY_LAND_MINE_COUNTS[row][col] = count; continue; } - landMineCounts[i][j] = 0; - } - } - while (true) { - System.out.println(" a b c d e f g h i j"); - for (int i = 0; i < 8; i++) { - System.out.printf("%d ", i + 1); - for (int j = 0; j < 10; j++) { - System.out.print(board[i][j] + " "); - } - System.out.println(); - } - if (gameStatus == 1) { - System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); - break; - } - if (gameStatus == -1) { - System.out.println("지뢰를 밟았습니다. GAME OVER!"); - break; - } - System.out.println(); - System.out.println("선택할 좌표를 입력하세요. (예: a1)"); - String input = scanner.nextLine(); - System.out.println("선택한 셀에 대한 행위를 선택하세요. (1: 오픈, 2: 깃발 꽂기)"); - String input2 = scanner.nextLine(); - char c = input.charAt(0); - char r = input.charAt(1); - int col; - switch (c) { - case 'a': - col = 0; - break; - case 'b': - col = 1; - break; - case 'c': - col = 2; - break; - case 'd': - col = 3; - break; - case 'e': - col = 4; - break; - case 'f': - col = 5; - break; - case 'g': - col = 6; - break; - case 'h': - col = 7; - break; - case 'i': - col = 8; - break; - case 'j': - col = 9; - break; - default: - col = -1; - break; - } - int row = Character.getNumericValue(r) - 1; - if (input2.equals("2")) { - board[row][col] = "⚑"; - boolean open = true; - for (int i = 0; i < 8; i++) { - for (int j = 0; j < 10; j++) { - if (board[i][j].equals("□")) { - open = false; - } - } - } - if (open) { - gameStatus = 1; - } - } else if (input2.equals("1")) { - if (landMines[row][col]) { - board[row][col] = "☼"; - gameStatus = -1; - continue; - } else { - open(row, col); - } - boolean open = true; - for (int i = 0; i < 8; i++) { - for (int j = 0; j < 10; j++) { - if (board[i][j].equals("□")) { - open = false; - } - } - } - if (open) { - gameStatus = 1; - } - } else { - System.out.println("잘못된 번호를 선택하셨습니다."); + NEAR_BY_LAND_MINE_COUNTS[row][col] = 0; } } } + private static void showGameStartCommand() { + System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + System.out.println("지뢰찾기 게임 시작!"); + System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + } + private static void open(int row, int col) { - if (row < 0 || row >= 8 || col < 0 || col >= 10) { + if (row < 0 || row >= BOARD_ROW_SIZE || col < 0 || col >= BOARD_COL_SIZE) { return; } - if (!board[row][col].equals("□")) { + if (!BOARD[row][col].equals(CLOSED_CELL_SIGN)) { return; } - if (landMines[row][col]) { + if (isLandMineCell(row, col)) { return; } - if (landMineCounts[row][col] != 0) { - board[row][col] = String.valueOf(landMineCounts[row][col]); + if (NEAR_BY_LAND_MINE_COUNTS[row][col] != 0) { + BOARD[row][col] = String.valueOf(NEAR_BY_LAND_MINE_COUNTS[row][col]); return; } else { - board[row][col] = "■"; + BOARD[row][col] = OPENED_CELL_SIGN; } + + // 재귀임 open(row - 1, col - 1); open(row - 1, col); open(row - 1, col + 1); @@ -184,4 +252,4 @@ private static void open(int row, int col) { open(row + 1, col + 1); } -} +} \ No newline at end of file From bab8e936cebb40aec94874fabb1456b277a15854 Mon Sep 17 00:00:00 2001 From: devny Date: Tue, 4 Mar 2025 11:23:43 +0900 Subject: [PATCH 02/39] 2025 03 04 by devny - Early return --- .../minesweeper/tobe/MinesweeperGame.java | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index 783ad92b0..6ef790cb8 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -38,25 +38,34 @@ public static void main(String[] args) { String cellInput = getCellInputFromUser(scanner); String userActionInput = getUserActionInputFromUser(scanner); - int selectedColIndex = getSelectedColIndex(cellInput); - int selectedRowIndex = getSelectedRowIndex(cellInput); - - if (doesUserChooseToPlantFlag(userActionInput)) { - BOARD[selectedRowIndex][selectedColIndex] = FLAG_SIGN; - checkIfGameIsOver(); - } else if (doesUserChooseToOpenCell(userActionInput)) { - if (isLandMineCell(selectedRowIndex, selectedColIndex)) { - BOARD[selectedRowIndex][selectedColIndex] = LAND_MINE_SIGN; - changeGameStatusToLose(); - continue; - } else { - open(selectedRowIndex, selectedColIndex); - } - checkIfGameIsOver(); - } else { - System.out.println("잘못된 번호를 선택하셨습니다."); + actOnCell(cellInput, userActionInput); + } + } + + private static void actOnCell(String cellInput, String userActionInput) { + int selectedColIndex = getSelectedColIndex(cellInput); + int selectedRowIndex = getSelectedRowIndex(cellInput); + + if (doesUserChooseToPlantFlag(userActionInput)) { + BOARD[selectedRowIndex][selectedColIndex] = FLAG_SIGN; + checkIfGameIsOver(); + + return; + } + + if (doesUserChooseToOpenCell(userActionInput)) { + if (isLandMineCell(selectedRowIndex, selectedColIndex)) { + BOARD[selectedRowIndex][selectedColIndex] = LAND_MINE_SIGN; + changeGameStatusToLose(); + return; } + + open(selectedRowIndex, selectedColIndex); + checkIfGameIsOver(); + return; } + + System.out.println("잘못된 번호를 선택하셨습니다."); } private static void changeGameStatusToLose() { From cf602eb4efb7798fd01c8b8bf24a9e7d2c1598df Mon Sep 17 00:00:00 2001 From: devny Date: Tue, 4 Mar 2025 16:32:43 +0900 Subject: [PATCH 03/39] =?UTF-8?q?2025=2003=2004=20by=20devny=20-=203?= =?UTF-8?q?=EC=A4=91=20depth=20=EB=A5=BC=20=ED=95=B4=EC=86=8C=ED=96=88?= =?UTF-8?q?=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/MinesweeperGame.java | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index 6ef790cb8..e5df8c1e7 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -1,5 +1,6 @@ package cleancode.minesweeper.tobe; +import java.util.Arrays; import java.util.Random; import java.util.Scanner; @@ -124,16 +125,24 @@ private static void changeGameStatusToWin() { gameStatus = 1; } +// private static boolean isAllCellOpened() { +// boolean isAllOpened = true; +// for (int row = 0; row < BOARD_ROW_SIZE; row++) { +// for (int col = 0; col < BOARD_COL_SIZE; col++) { +// if (BOARD[row][col].equals(CLOSED_CELL_SIGN)) { // 네모 박스가 하나라도 있는지 확인 +// isAllOpened = false; +// } +// } +// } +// return isAllOpened; +// } + private static boolean isAllCellOpened() { - boolean isAllOpened = true; - for (int row = 0; row < BOARD_ROW_SIZE; row++) { - for (int col = 0; col < BOARD_COL_SIZE; col++) { - if (BOARD[row][col].equals(CLOSED_CELL_SIGN)) { // 네모 박스가 하나라도 있는지 확인 - isAllOpened = false; - } - } - } - return isAllOpened; + return Arrays.stream(BOARD)// BOARD 라는 이중 string 배열에 stream 을 걸면 String[] 형태의 Stream 이 나옴 Stream + // 그냥 Map 을 하면 Stream> 이 나오는데 flatMap 을 하면서 평탄화를 통해 이중배열을 배열로, 즉, Stream 으로 만들어주는 것 + .flatMap(stringArr -> Arrays.stream(stringArr)) // flatMap 을 하면 Stream 이 생기는데 이 stringArray 를 하나씩 돌면서 다시 Stream 만들거다 + // 여기까지가 Stream + .noneMatch(cell -> cell.equals(CLOSED_CELL_SIGN)); } private static int convertRowFrom(char cellInputRow) { From 15cdced3c2891aa268e6cdb482ee9815d95f3b78 Mon Sep 17 00:00:00 2001 From: devny Date: Tue, 4 Mar 2025 16:36:37 +0900 Subject: [PATCH 04/39] =?UTF-8?q?2025=2003=2004=20by=20devny=20-=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=20=EA=B3=B3=20=EA=B0=80?= =?UTF-8?q?=EA=B9=8C=EC=9A=B4=20=EA=B3=B3=EC=97=90=EC=84=9C=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=EB=A5=BC=20=EC=84=A0=EC=96=B8=ED=95=98=EC=9E=90!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - while 문 안에서 스캐너가 계속 생성되기 때문에 상수로 선언해 줌. --- .../minesweeper/tobe/MinesweeperGame.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index e5df8c1e7..994c8b709 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -9,6 +9,7 @@ public class MinesweeperGame { // 중요한 문자열, 숫자야. 유지보수할때 잘 봐야해! 할 수 있는 것 = 매직넘버, 매직스트링 public static final int BOARD_ROW_SIZE = 8; public static final int BOARD_COL_SIZE = 10; + public static final Scanner SCANNER = new Scanner(System.in); // 상수 컨벤션 = 대문자와 언더스코어로 이루어져 있게 해야함 private static final String[][] BOARD = new String[BOARD_ROW_SIZE][BOARD_COL_SIZE]; private static final Integer[][] NEAR_BY_LAND_MINE_COUNTS = new Integer[BOARD_ROW_SIZE][BOARD_COL_SIZE]; @@ -23,7 +24,6 @@ public class MinesweeperGame { public static void main(String[] args) { showGameStartCommand(); - Scanner scanner = new Scanner(System.in); initializeGame(); while (true) { showBoard(); @@ -36,8 +36,8 @@ public static void main(String[] args) { break; } System.out.println(); - String cellInput = getCellInputFromUser(scanner); - String userActionInput = getUserActionInputFromUser(scanner); + String cellInput = getCellInputFromUser(); + String userActionInput = getUserActionInputFromUser(); actOnCell(cellInput, userActionInput); } @@ -95,15 +95,15 @@ private static int getSelectedColIndex(String cellInput) { return convertColFrom(cellInputCol); } - private static String getUserActionInputFromUser(Scanner scanner) { + private static String getUserActionInputFromUser() { System.out.println("선택한 셀에 대한 행위를 선택하세요. (1: 오픈, 2: 깃발 꽂기)"); - return scanner.nextLine(); + return SCANNER.nextLine(); } - private static String getCellInputFromUser(Scanner scanner) { + private static String getCellInputFromUser() { System.out.println("선택할 좌표를 입력하세요. (예: a1)"); - return scanner.nextLine(); + return SCANNER.nextLine(); } private static boolean doesUserLoseTheGame() { From 9d183b7bff03594a79539c06fb6fab36081ccbcf Mon Sep 17 00:00:00 2001 From: devny Date: Wed, 5 Mar 2025 09:25:37 +0900 Subject: [PATCH 05/39] =?UTF-8?q?2025=2003=2005=20by=20devny=20-=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/MinesweeperGame.java | 108 +++++++++++------- 1 file changed, 65 insertions(+), 43 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index 994c8b709..f0a67b744 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -25,21 +25,28 @@ public class MinesweeperGame { public static void main(String[] args) { showGameStartCommand(); initializeGame(); + while (true) { - showBoard(); - if (doesUserWinTheGame()) { - System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); - break; - } - if (doesUserLoseTheGame()) { - System.out.println("지뢰를 밟았습니다. GAME OVER!"); - break; - } - System.out.println(); - String cellInput = getCellInputFromUser(); - String userActionInput = getUserActionInputFromUser(); + try { + showBoard(); + + if (doesUserWinTheGame()) { + System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); + break; + } + if (doesUserLoseTheGame()) { + System.out.println("지뢰를 밟았습니다. GAME OVER!"); + break; + } - actOnCell(cellInput, userActionInput); + System.out.println(); + String cellInput = getCellInputFromUser(); + String userActionInput = getUserActionInputFromUser(); + + actOnCell(cellInput, userActionInput); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } } } @@ -142,11 +149,15 @@ private static boolean isAllCellOpened() { // 그냥 Map 을 하면 Stream> 이 나오는데 flatMap 을 하면서 평탄화를 통해 이중배열을 배열로, 즉, Stream 으로 만들어주는 것 .flatMap(stringArr -> Arrays.stream(stringArr)) // flatMap 을 하면 Stream 이 생기는데 이 stringArray 를 하나씩 돌면서 다시 Stream 만들거다 // 여기까지가 Stream - .noneMatch(cell -> cell.equals(CLOSED_CELL_SIGN)); + .noneMatch(cell -> CLOSED_CELL_SIGN.equals(cell)); } private static int convertRowFrom(char cellInputRow) { - return Character.getNumericValue(cellInputRow) - 1; + int rowIndex = Character.getNumericValue(cellInputRow) - 1; + if (rowIndex > BOARD_ROW_SIZE) { + throw new IllegalArgumentException("잘못된 입력입니다."); + } + return rowIndex; } private static int convertColFrom(char cellInputCol) { @@ -173,7 +184,8 @@ private static int convertColFrom(char cellInputCol) { case 'j': return 9; default: - return -1; +// return -1; + throw new IllegalArgumentException("잘못된 입력입니다."); } // return selectedColIndex; } @@ -195,47 +207,54 @@ private static void initializeGame() { BOARD[row][col] = CLOSED_CELL_SIGN; } } + for (int i = 0; i < LAND_MINES_COUNT; i++) { // 10 이 col 숫자가아니라 지뢰숫자 int col = new Random().nextInt(BOARD_COL_SIZE); int row = new Random().nextInt(BOARD_ROW_SIZE); LAND_MINES[row][col] = true; } + for (int row = 0; row < BOARD_ROW_SIZE; row++) { for (int col = 0; col < BOARD_COL_SIZE; col++) { int count = 0; - if (!isLandMineCell(row, col)) { - if (row - 1 >= 0 && col - 1 >= 0 && isLandMineCell(row - 1,col - 1)) { - count++; - } - if (row - 1 >= 0 && isLandMineCell(row - 1, col)) { - count++; - } - if (row - 1 >= 0 && col + 1 < BOARD_COL_SIZE && isLandMineCell(row - 1, col + 1)) { - count++; - } - if (col - 1 >= 0 && isLandMineCell(row, col - 1)) { - count++; - } - if (col + 1 < BOARD_COL_SIZE && isLandMineCell(row, col + 1)) { - count++; - } - if (row + 1 < BOARD_ROW_SIZE && col - 1 >= 0 && isLandMineCell(row + 1, col - 1)) { - count++; - } - if (row + 1 < BOARD_ROW_SIZE && isLandMineCell(row + 1, col)) { - count++; - } - if (row + 1 < BOARD_ROW_SIZE && col + 1 < BOARD_COL_SIZE && isLandMineCell(row + 1, col + 1)) { - count++; - } - NEAR_BY_LAND_MINE_COUNTS[row][col] = count; + if (isLandMineCell(row, col)) { + NEAR_BY_LAND_MINE_COUNTS[row][col] = 0; continue; } - NEAR_BY_LAND_MINE_COUNTS[row][col] = 0; + count = countNearbyLandMines(row, col, count); + NEAR_BY_LAND_MINE_COUNTS[row][col] = count; } } } + private static int countNearbyLandMines(int row, int col, int count) { + if (row - 1 >= 0 && col - 1 >= 0 && isLandMineCell(row - 1, col - 1)) { + count++; + } + if (row - 1 >= 0 && isLandMineCell(row - 1, col)) { + count++; + } + if (row - 1 >= 0 && col + 1 < BOARD_COL_SIZE && isLandMineCell(row - 1, col + 1)) { + count++; + } + if (col - 1 >= 0 && isLandMineCell(row, col - 1)) { + count++; + } + if (col + 1 < BOARD_COL_SIZE && isLandMineCell(row, col + 1)) { + count++; + } + if (row + 1 < BOARD_ROW_SIZE && col - 1 >= 0 && isLandMineCell(row + 1, col - 1)) { + count++; + } + if (row + 1 < BOARD_ROW_SIZE && isLandMineCell(row + 1, col)) { + count++; + } + if (row + 1 < BOARD_ROW_SIZE && col + 1 < BOARD_COL_SIZE && isLandMineCell(row + 1, col + 1)) { + count++; + } + return count; + } + private static void showGameStartCommand() { System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); System.out.println("지뢰찾기 게임 시작!"); @@ -246,12 +265,15 @@ private static void open(int row, int col) { if (row < 0 || row >= BOARD_ROW_SIZE || col < 0 || col >= BOARD_COL_SIZE) { return; } + if (!BOARD[row][col].equals(CLOSED_CELL_SIGN)) { return; } + if (isLandMineCell(row, col)) { return; } + if (NEAR_BY_LAND_MINE_COUNTS[row][col] != 0) { BOARD[row][col] = String.valueOf(NEAR_BY_LAND_MINE_COUNTS[row][col]); return; From c278d55589f89f14b41ff394508d994fd5d4b814 Mon Sep 17 00:00:00 2001 From: devny Date: Wed, 5 Mar 2025 11:17:53 +0900 Subject: [PATCH 06/39] =?UTF-8?q?2025=2003=2005=20by=20devny=20-=20?= =?UTF-8?q?=EC=9D=98=EB=8F=84=EC=A0=81=EC=9D=B8/=EB=B9=84=EC=9D=98?= =?UTF-8?q?=EB=8F=84=EC=A0=81=EC=9D=B8=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=B6=94=EA=B0=80=20+=20=EC=9D=98=EB=8F=84?= =?UTF-8?q?=EC=A0=81=EC=9D=B8=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC:=20Ap?= =?UTF-8?q?pException.java?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cleancode/minesweeper/tobe/AppException.java | 7 +++++++ .../java/cleancode/minesweeper/tobe/MinesweeperGame.java | 9 ++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/AppException.java diff --git a/src/main/java/cleancode/minesweeper/tobe/AppException.java b/src/main/java/cleancode/minesweeper/tobe/AppException.java new file mode 100644 index 000000000..fb4a1f638 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/AppException.java @@ -0,0 +1,7 @@ +package cleancode.minesweeper.tobe; + +public class AppException extends RuntimeException { + public AppException(String message) { + super(message); + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index f0a67b744..8d1941f9b 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -44,8 +44,11 @@ public static void main(String[] args) { String userActionInput = getUserActionInputFromUser(); actOnCell(cellInput, userActionInput); - } catch (IllegalArgumentException e) { + } catch (AppException e) { // 의도적인 Exception System.out.println(e.getMessage()); + } catch (Exception e) { // 예상하지 못한 Exception + System.out.println("프로그램에 문제가 생겼습니다."); +// e.printStackTrace(); // 실무에서는 Antipattern 실무에서는 log 시스템에서 log 를 남기고 별도의 조치를 취함 } } } @@ -155,7 +158,7 @@ private static boolean isAllCellOpened() { private static int convertRowFrom(char cellInputRow) { int rowIndex = Character.getNumericValue(cellInputRow) - 1; if (rowIndex > BOARD_ROW_SIZE) { - throw new IllegalArgumentException("잘못된 입력입니다."); + throw new AppException("잘못된 입력입니다."); } return rowIndex; } @@ -185,7 +188,7 @@ private static int convertColFrom(char cellInputCol) { return 9; default: // return -1; - throw new IllegalArgumentException("잘못된 입력입니다."); + throw new AppException("잘못된 입력입니다."); } // return selectedColIndex; } From 4dd97dd44cc63de8cfb3d43a97e7bb3bae834d09 Mon Sep 17 00:00:00 2001 From: devny Date: Wed, 5 Mar 2025 16:55:53 +0900 Subject: [PATCH 07/39] =?UTF-8?q?2025=2003=2005=20by=20devny=20-=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EC=9D=B4=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EC=B6=94=EC=83=81=ED=99=94=20=EC=A7=84=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit + Cell 객체 생성 + BOARD 를 Cell 객체 타입으로 변경 + 새로운 도메인 지식을 발견했을때 과감하게 사고 전환(isOpened -> isChecked) + Cell 객체에서 물어볼 수 있는 것들은 Cell 객체에 물어봄 --- .../java/cleancode/minesweeper/tobe/Cell.java | 87 +++++++++++++++++++ .../minesweeper/tobe/MinesweeperGame.java | 46 +++++----- 2 files changed, 108 insertions(+), 25 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/Cell.java diff --git a/src/main/java/cleancode/minesweeper/tobe/Cell.java b/src/main/java/cleancode/minesweeper/tobe/Cell.java new file mode 100644 index 000000000..43bfe1515 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/Cell.java @@ -0,0 +1,87 @@ +package cleancode.minesweeper.tobe; + +public class Cell { + private static final String FLAG_SIGN = "⚑"; + private static final String LAND_MINE_SIGN = "☼"; + private static final String UNCHECKED_SIGN = "□"; + private static final String EMPTY_SIGN = "■"; + + // 보드를 기반으로 먼저 만들어 보겠다. + // final 인 데이터는 최대한 final 로 만들어야 함 + private int nearbyLandMineCount; // 주변 지뢰 수 + private boolean isLandMine; // 지뢰여부 + private boolean isFlagged; + private boolean isOpened; + // Cell 이 가진 속성: 근처 지뢰 숫자, 지뢰 여부 + // Cell의 상태: 깃발 유무, 열렸다/닫혔다, 사용자가 확인함 + + private Cell(int nearbyLandMineCount, boolean isLandMine, boolean isFlagged, boolean isOpened) { + this.nearbyLandMineCount = nearbyLandMineCount; + this.isLandMine = isLandMine; + this.isFlagged = isFlagged; + this.isOpened = isOpened; + } + + // new Cell 보다 정적팩토리 메서드를 즐겨씀 why? 이름을 부여해줄 수 있기 때문에 + // 그리고 생성자는 private 으로 변경 + // 결과: 정적패토리 메서드가 생성자 역할을 할 수 있도록 함 + public static Cell of(int nearbyLandMineCount, boolean isLandMine, boolean isFlagged, boolean isOpened) { + return new Cell(nearbyLandMineCount, isLandMine, isFlagged, isOpened); + } + + public static Cell create() { + return of(0, false, false, false); + } + + public void turnOnLandMine() { + this.isLandMine = true; + } + + public void flag() { + this.isFlagged = true; + } + + public void open() { + this.isOpened = true; + } + + public boolean isChecked() { + return isFlagged || isOpened; // 닫혀있는데 깃발이 꽂혀있거나, 열었거나 + } + + public boolean isLandMine() { + return isLandMine; + } + + public boolean isOpened() { + return isOpened; + } + + public boolean hasLandMineCount() { + return this.nearbyLandMineCount != 0; + } + + public void updateNearbyLandMineCount(int count) { + this.nearbyLandMineCount = count; + } + + public String getSign() { + if (isOpened) { + if (isLandMine) { + return LAND_MINE_SIGN; + } + + if (hasLandMineCount()) { + return String.valueOf(nearbyLandMineCount); + } + + return EMPTY_SIGN; + } + + if (isFlagged) { + return FLAG_SIGN; + } + + return UNCHECKED_SIGN; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index 8d1941f9b..ed65a5189 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -11,14 +11,8 @@ public class MinesweeperGame { public static final int BOARD_COL_SIZE = 10; public static final Scanner SCANNER = new Scanner(System.in); // 상수 컨벤션 = 대문자와 언더스코어로 이루어져 있게 해야함 - private static final String[][] BOARD = new String[BOARD_ROW_SIZE][BOARD_COL_SIZE]; - private static final Integer[][] NEAR_BY_LAND_MINE_COUNTS = new Integer[BOARD_ROW_SIZE][BOARD_COL_SIZE]; - private static final boolean[][] LAND_MINES = new boolean[BOARD_ROW_SIZE][BOARD_COL_SIZE]; + private static final Cell[][] BOARD = new Cell[BOARD_ROW_SIZE][BOARD_COL_SIZE]; public static final int LAND_MINES_COUNT = 10; - public static final String FLAG_SIGN = "⚑"; - public static final String LAND_MINE_SIGN = "☼"; - public static final String CLOSED_CELL_SIGN = "□"; - public static final String OPENED_CELL_SIGN = "■"; private static int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 @@ -58,7 +52,7 @@ private static void actOnCell(String cellInput, String userActionInput) { int selectedRowIndex = getSelectedRowIndex(cellInput); if (doesUserChooseToPlantFlag(userActionInput)) { - BOARD[selectedRowIndex][selectedColIndex] = FLAG_SIGN; + BOARD[selectedRowIndex][selectedColIndex].flag(); checkIfGameIsOver(); return; @@ -66,7 +60,7 @@ private static void actOnCell(String cellInput, String userActionInput) { if (doesUserChooseToOpenCell(userActionInput)) { if (isLandMineCell(selectedRowIndex, selectedColIndex)) { - BOARD[selectedRowIndex][selectedColIndex] = LAND_MINE_SIGN; + BOARD[selectedRowIndex][selectedColIndex].open(); changeGameStatusToLose(); return; } @@ -84,7 +78,7 @@ private static void changeGameStatusToLose() { } private static boolean isLandMineCell(int selectedRowIndex, int selectedColIndex) { - return LAND_MINES[selectedRowIndex][selectedColIndex]; + return BOARD[selectedRowIndex][selectedColIndex].isLandMine(); } private static boolean doesUserChooseToOpenCell(String userActionInput) { @@ -125,8 +119,8 @@ private static boolean doesUserWinTheGame() { } private static void checkIfGameIsOver() { - boolean isAllOpened = isAllCellOpened(); - if (isAllOpened) { // 게임 이긴 것 + boolean isAllCellChecked = isAllCellChecked(); + if (isAllCellChecked) { // 게임 이긴 것 changeGameStatusToWin(); } } @@ -147,12 +141,12 @@ private static void changeGameStatusToWin() { // return isAllOpened; // } - private static boolean isAllCellOpened() { + private static boolean isAllCellChecked() { return Arrays.stream(BOARD)// BOARD 라는 이중 string 배열에 stream 을 걸면 String[] 형태의 Stream 이 나옴 Stream // 그냥 Map 을 하면 Stream> 이 나오는데 flatMap 을 하면서 평탄화를 통해 이중배열을 배열로, 즉, Stream 으로 만들어주는 것 .flatMap(stringArr -> Arrays.stream(stringArr)) // flatMap 을 하면 Stream 이 생기는데 이 stringArray 를 하나씩 돌면서 다시 Stream 만들거다 // 여기까지가 Stream - .noneMatch(cell -> CLOSED_CELL_SIGN.equals(cell)); + .allMatch(Cell::isChecked); } private static int convertRowFrom(char cellInputRow) { @@ -198,7 +192,7 @@ private static void showBoard() { for (int row = 0; row < BOARD_ROW_SIZE; row++) { System.out.printf("%d ", row + 1); for (int col = 0; col < BOARD_COL_SIZE; col++) { - System.out.print(BOARD[row][col] + " "); + System.out.print(BOARD[row][col].getSign() + " "); // 여기는 getter 를 안쓰는게 이상해 // 내가 여기에 보드를 그릴테니 cell 내용을 줘! } System.out.println(); } @@ -207,25 +201,25 @@ private static void showBoard() { private static void initializeGame() { for (int row = 0; row < BOARD_ROW_SIZE; row++) { for (int col = 0; col < BOARD_COL_SIZE; col++) { - BOARD[row][col] = CLOSED_CELL_SIGN; + BOARD[row][col] = Cell.create(); } } for (int i = 0; i < LAND_MINES_COUNT; i++) { // 10 이 col 숫자가아니라 지뢰숫자 int col = new Random().nextInt(BOARD_COL_SIZE); int row = new Random().nextInt(BOARD_ROW_SIZE); - LAND_MINES[row][col] = true; + BOARD[row][col].turnOnLandMine(); } for (int row = 0; row < BOARD_ROW_SIZE; row++) { for (int col = 0; col < BOARD_COL_SIZE; col++) { int count = 0; if (isLandMineCell(row, col)) { - NEAR_BY_LAND_MINE_COUNTS[row][col] = 0; + // 이미 셀의 기본속성을 0으로 바꿔서 따로 안해도 됨 continue; } count = countNearbyLandMines(row, col, count); - NEAR_BY_LAND_MINE_COUNTS[row][col] = count; + BOARD[row][col].updateNearbyLandMineCount(count); } } } @@ -269,7 +263,7 @@ private static void open(int row, int col) { return; } - if (!BOARD[row][col].equals(CLOSED_CELL_SIGN)) { + if (BOARD[row][col].isOpened()) { return; } @@ -277,14 +271,16 @@ private static void open(int row, int col) { return; } - if (NEAR_BY_LAND_MINE_COUNTS[row][col] != 0) { - BOARD[row][col] = String.valueOf(NEAR_BY_LAND_MINE_COUNTS[row][col]); + // 여기까지 안열렸으면 아직 안열린 cell 이니까 열어! + BOARD[row][col].open(); // 오픈 + + if (BOARD[row][col].hasLandMineCount()) { // 숫자가 있으면! + // 열고 숫자를 초기화 한 것임 +// BOARD[row][col] = Cell.ofNearbyLandMineCount(NEAR_BY_LAND_MINE_COUNTS[row][col]); return; - } else { - BOARD[row][col] = OPENED_CELL_SIGN; } - // 재귀임 + // 재귀임 // 오픈을 시작하고 멈추는건 숫자일때, 지뢰일때임 open(row - 1, col - 1); open(row - 1, col); open(row - 1, col + 1); From efe59b3fe48a7b46d94b3bee2ed2263306ea9b80 Mon Sep 17 00:00:00 2001 From: devny Date: Thu, 6 Mar 2025 13:58:35 +0900 Subject: [PATCH 08/39] =?UTF-8?q?2025=2003=2006=20by=20devny=20-=20SRP:=20?= =?UTF-8?q?=EB=8B=A8=EC=9D=BC=20=EC=B1=85=EC=9E=84=20=EC=9B=90=EC=B9=991?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - main 에서는 게임 실행만 하도록 변경 - 지뢰찾기 class 를 만들어 지뢰찾기 게임 class 는 따로 실행되도록 함 + Minesweeper.java 에서 이제 main 에서 실행되지 않으므로 함수들 static 삭제 --- .../minesweeper/tobe/GameApplication.java | 9 +++ ...{MinesweeperGame.java => Minesweeper.java} | 55 +++++++++---------- 2 files changed, 36 insertions(+), 28 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/GameApplication.java rename src/main/java/cleancode/minesweeper/tobe/{MinesweeperGame.java => Minesweeper.java} (86%) diff --git a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java new file mode 100644 index 000000000..a4cf65a4d --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java @@ -0,0 +1,9 @@ +package cleancode.minesweeper.tobe; + +public class GameApplication { + + public static void main(String[] args) { + Minesweeper minesweeper = new Minesweeper(); + minesweeper.run(); + } +} \ No newline at end of file diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java similarity index 86% rename from src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java rename to src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index ed65a5189..1a6b5566a 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -4,8 +4,8 @@ import java.util.Random; import java.util.Scanner; -public class MinesweeperGame { - +// 모든 지뢰찾기 게임 로직을 여기에 둘 것임 +public class Minesweeper { // 중요한 문자열, 숫자야. 유지보수할때 잘 봐야해! 할 수 있는 것 = 매직넘버, 매직스트링 public static final int BOARD_ROW_SIZE = 8; public static final int BOARD_COL_SIZE = 10; @@ -14,9 +14,9 @@ public class MinesweeperGame { private static final Cell[][] BOARD = new Cell[BOARD_ROW_SIZE][BOARD_COL_SIZE]; public static final int LAND_MINES_COUNT = 10; - private static int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 + private int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 - public static void main(String[] args) { + public void run() { showGameStartCommand(); initializeGame(); @@ -47,7 +47,7 @@ public static void main(String[] args) { } } - private static void actOnCell(String cellInput, String userActionInput) { + private void actOnCell(String cellInput, String userActionInput) { int selectedColIndex = getSelectedColIndex(cellInput); int selectedRowIndex = getSelectedRowIndex(cellInput); @@ -73,63 +73,63 @@ private static void actOnCell(String cellInput, String userActionInput) { System.out.println("잘못된 번호를 선택하셨습니다."); } - private static void changeGameStatusToLose() { + private void changeGameStatusToLose() { gameStatus = -1; } - private static boolean isLandMineCell(int selectedRowIndex, int selectedColIndex) { + private boolean isLandMineCell(int selectedRowIndex, int selectedColIndex) { return BOARD[selectedRowIndex][selectedColIndex].isLandMine(); } - private static boolean doesUserChooseToOpenCell(String userActionInput) { + private boolean doesUserChooseToOpenCell(String userActionInput) { return userActionInput.equals("1"); } - private static boolean doesUserChooseToPlantFlag(String userActionInput) { + private boolean doesUserChooseToPlantFlag(String userActionInput) { return userActionInput.equals("2"); } - private static int getSelectedRowIndex(String cellInput) { + private int getSelectedRowIndex(String cellInput) { char cellInputRow = cellInput.charAt(1); return convertRowFrom(cellInputRow); } - private static int getSelectedColIndex(String cellInput) { + private int getSelectedColIndex(String cellInput) { char cellInputCol = cellInput.charAt(0); return convertColFrom(cellInputCol); } - private static String getUserActionInputFromUser() { + private String getUserActionInputFromUser() { System.out.println("선택한 셀에 대한 행위를 선택하세요. (1: 오픈, 2: 깃발 꽂기)"); return SCANNER.nextLine(); } - private static String getCellInputFromUser() { + private String getCellInputFromUser() { System.out.println("선택할 좌표를 입력하세요. (예: a1)"); return SCANNER.nextLine(); } - private static boolean doesUserLoseTheGame() { + private boolean doesUserLoseTheGame() { return gameStatus == -1; } - private static boolean doesUserWinTheGame() { + private boolean doesUserWinTheGame() { return gameStatus == 1; } - private static void checkIfGameIsOver() { + private void checkIfGameIsOver() { boolean isAllCellChecked = isAllCellChecked(); if (isAllCellChecked) { // 게임 이긴 것 changeGameStatusToWin(); } } - private static void changeGameStatusToWin() { + private void changeGameStatusToWin() { gameStatus = 1; } -// private static boolean isAllCellOpened() { +// private boolean isAllCellOpened() { // boolean isAllOpened = true; // for (int row = 0; row < BOARD_ROW_SIZE; row++) { // for (int col = 0; col < BOARD_COL_SIZE; col++) { @@ -141,7 +141,7 @@ private static void changeGameStatusToWin() { // return isAllOpened; // } - private static boolean isAllCellChecked() { + private boolean isAllCellChecked() { return Arrays.stream(BOARD)// BOARD 라는 이중 string 배열에 stream 을 걸면 String[] 형태의 Stream 이 나옴 Stream // 그냥 Map 을 하면 Stream> 이 나오는데 flatMap 을 하면서 평탄화를 통해 이중배열을 배열로, 즉, Stream 으로 만들어주는 것 .flatMap(stringArr -> Arrays.stream(stringArr)) // flatMap 을 하면 Stream 이 생기는데 이 stringArray 를 하나씩 돌면서 다시 Stream 만들거다 @@ -149,7 +149,7 @@ private static boolean isAllCellChecked() { .allMatch(Cell::isChecked); } - private static int convertRowFrom(char cellInputRow) { + private int convertRowFrom(char cellInputRow) { int rowIndex = Character.getNumericValue(cellInputRow) - 1; if (rowIndex > BOARD_ROW_SIZE) { throw new AppException("잘못된 입력입니다."); @@ -157,7 +157,7 @@ private static int convertRowFrom(char cellInputRow) { return rowIndex; } - private static int convertColFrom(char cellInputCol) { + private int convertColFrom(char cellInputCol) { // int selectedColIndex; switch (cellInputCol) { case 'a': @@ -187,7 +187,7 @@ private static int convertColFrom(char cellInputCol) { // return selectedColIndex; } - private static void showBoard() { + private void showBoard() { System.out.println(" a b c d e f g h i j"); for (int row = 0; row < BOARD_ROW_SIZE; row++) { System.out.printf("%d ", row + 1); @@ -198,7 +198,7 @@ private static void showBoard() { } } - private static void initializeGame() { + private void initializeGame() { for (int row = 0; row < BOARD_ROW_SIZE; row++) { for (int col = 0; col < BOARD_COL_SIZE; col++) { BOARD[row][col] = Cell.create(); @@ -224,7 +224,7 @@ private static void initializeGame() { } } - private static int countNearbyLandMines(int row, int col, int count) { + private int countNearbyLandMines(int row, int col, int count) { if (row - 1 >= 0 && col - 1 >= 0 && isLandMineCell(row - 1, col - 1)) { count++; } @@ -252,13 +252,13 @@ private static int countNearbyLandMines(int row, int col, int count) { return count; } - private static void showGameStartCommand() { + private void showGameStartCommand() { System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); System.out.println("지뢰찾기 게임 시작!"); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); } - private static void open(int row, int col) { + private void open(int row, int col) { if (row < 0 || row >= BOARD_ROW_SIZE || col < 0 || col >= BOARD_COL_SIZE) { return; } @@ -290,5 +290,4 @@ private static void open(int row, int col) { open(row + 1, col); open(row + 1, col + 1); } - -} \ No newline at end of file +} From 7a3bd1e43ea51cc1b4a7e7b7a9429fbb7c8e92e2 Mon Sep 17 00:00:00 2001 From: devny Date: Thu, 6 Mar 2025 16:19:17 +0900 Subject: [PATCH 09/39] =?UTF-8?q?2025=2003=2006=20by=20devny=20-=20SRP:=20?= =?UTF-8?q?=EB=8B=A8=EC=9D=BC=20=EC=B1=85=EC=9E=84=20=EC=9B=90=EC=B9=992?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SRP에 따라서 사용자 입출력을 나눠봤음. --- .../minesweeper/tobe/AppException.java | 7 --- .../minesweeper/tobe/GameException.java | 7 +++ .../minesweeper/tobe/Minesweeper.java | 54 ++++++++----------- .../tobe/io/ConsoleInputHandler.java | 11 ++++ .../tobe/io/ConsoleOutputHandler.java | 48 +++++++++++++++++ 5 files changed, 87 insertions(+), 40 deletions(-) delete mode 100644 src/main/java/cleancode/minesweeper/tobe/AppException.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/GameException.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java diff --git a/src/main/java/cleancode/minesweeper/tobe/AppException.java b/src/main/java/cleancode/minesweeper/tobe/AppException.java deleted file mode 100644 index fb4a1f638..000000000 --- a/src/main/java/cleancode/minesweeper/tobe/AppException.java +++ /dev/null @@ -1,7 +0,0 @@ -package cleancode.minesweeper.tobe; - -public class AppException extends RuntimeException { - public AppException(String message) { - super(message); - } -} diff --git a/src/main/java/cleancode/minesweeper/tobe/GameException.java b/src/main/java/cleancode/minesweeper/tobe/GameException.java new file mode 100644 index 000000000..c0e34c4ac --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/GameException.java @@ -0,0 +1,7 @@ +package cleancode.minesweeper.tobe; + +public class GameException extends RuntimeException { + public GameException(String message) { + super(message); + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index 1a6b5566a..1b6ee8e60 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -1,47 +1,52 @@ package cleancode.minesweeper.tobe; +import cleancode.minesweeper.tobe.io.ConsoleInputHandler; +import cleancode.minesweeper.tobe.io.ConsoleOutputHandler; + import java.util.Arrays; import java.util.Random; -import java.util.Scanner; // 모든 지뢰찾기 게임 로직을 여기에 둘 것임 public class Minesweeper { // 중요한 문자열, 숫자야. 유지보수할때 잘 봐야해! 할 수 있는 것 = 매직넘버, 매직스트링 public static final int BOARD_ROW_SIZE = 8; public static final int BOARD_COL_SIZE = 10; - public static final Scanner SCANNER = new Scanner(System.in); // 상수 컨벤션 = 대문자와 언더스코어로 이루어져 있게 해야함 private static final Cell[][] BOARD = new Cell[BOARD_ROW_SIZE][BOARD_COL_SIZE]; public static final int LAND_MINES_COUNT = 10; + // 입출력에 대한건 여기서! + private final ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(); + private final ConsoleOutputHandler consoleOutputHandler = new ConsoleOutputHandler(); + private int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 public void run() { - showGameStartCommand(); + consoleOutputHandler.showGameStartCommand(); initializeGame(); while (true) { try { - showBoard(); + consoleOutputHandler.showBoard(BOARD); if (doesUserWinTheGame()) { - System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); + consoleOutputHandler.printGameWinningComment(); break; } if (doesUserLoseTheGame()) { - System.out.println("지뢰를 밟았습니다. GAME OVER!"); + consoleOutputHandler.printGameLosingComment(); break; } - System.out.println(); String cellInput = getCellInputFromUser(); String userActionInput = getUserActionInputFromUser(); actOnCell(cellInput, userActionInput); - } catch (AppException e) { // 의도적인 Exception - System.out.println(e.getMessage()); + } catch (GameException e) { // 의도적인 Exception + consoleOutputHandler.printExceptionMessage(e); + } catch (Exception e) { // 예상하지 못한 Exception - System.out.println("프로그램에 문제가 생겼습니다."); + consoleOutputHandler.printSimpleMessage("프로그램에 문제가 생겼습니다."); // e.printStackTrace(); // 실무에서는 Antipattern 실무에서는 log 시스템에서 log 를 남기고 별도의 조치를 취함 } } @@ -100,14 +105,14 @@ private int getSelectedColIndex(String cellInput) { } private String getUserActionInputFromUser() { - System.out.println("선택한 셀에 대한 행위를 선택하세요. (1: 오픈, 2: 깃발 꽂기)"); - return SCANNER.nextLine(); + consoleOutputHandler.printCommentFOrUserAction(); + return consoleInputHandler.getUserInput(); } private String getCellInputFromUser() { - System.out.println("선택할 좌표를 입력하세요. (예: a1)"); + consoleOutputHandler.printCommentForSelectingCell(); - return SCANNER.nextLine(); + return consoleInputHandler.getUserInput(); } private boolean doesUserLoseTheGame() { @@ -152,7 +157,7 @@ private boolean isAllCellChecked() { private int convertRowFrom(char cellInputRow) { int rowIndex = Character.getNumericValue(cellInputRow) - 1; if (rowIndex > BOARD_ROW_SIZE) { - throw new AppException("잘못된 입력입니다."); + throw new GameException("잘못된 입력입니다."); } return rowIndex; } @@ -182,22 +187,11 @@ private int convertColFrom(char cellInputCol) { return 9; default: // return -1; - throw new AppException("잘못된 입력입니다."); + throw new GameException("잘못된 입력입니다."); } // return selectedColIndex; } - private void showBoard() { - System.out.println(" a b c d e f g h i j"); - for (int row = 0; row < BOARD_ROW_SIZE; row++) { - System.out.printf("%d ", row + 1); - for (int col = 0; col < BOARD_COL_SIZE; col++) { - System.out.print(BOARD[row][col].getSign() + " "); // 여기는 getter 를 안쓰는게 이상해 // 내가 여기에 보드를 그릴테니 cell 내용을 줘! - } - System.out.println(); - } - } - private void initializeGame() { for (int row = 0; row < BOARD_ROW_SIZE; row++) { for (int col = 0; col < BOARD_COL_SIZE; col++) { @@ -252,12 +246,6 @@ private int countNearbyLandMines(int row, int col, int count) { return count; } - private void showGameStartCommand() { - System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); - System.out.println("지뢰찾기 게임 시작!"); - System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); - } - private void open(int row, int col) { if (row < 0 || row >= BOARD_ROW_SIZE || col < 0 || col >= BOARD_COL_SIZE) { return; diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java new file mode 100644 index 000000000..39a79e283 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java @@ -0,0 +1,11 @@ +package cleancode.minesweeper.tobe.io; + +import java.util.Scanner; + +public class ConsoleInputHandler { + public static final Scanner SCANNER = new Scanner(System.in); + + public String getUserInput() { + return SCANNER.nextLine(); + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java new file mode 100644 index 000000000..4af26fda2 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -0,0 +1,48 @@ +package cleancode.minesweeper.tobe.io; + +import cleancode.minesweeper.tobe.GameException; +import cleancode.minesweeper.tobe.Cell; + +public class ConsoleOutputHandler { + public void showGameStartCommand() { + System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + System.out.println("지뢰찾기 게임 시작!"); + System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + } + + public void showBoard(Cell[][] board) { + System.out.println(" a b c d e f g h i j"); + for (int row = 0; row < board.length; row++) { + System.out.printf("%d ", row + 1); + for (int col = 0; col < board[0].length; col++) { + System.out.print(board[row][col].getSign() + " "); // 여기는 getter 를 안쓰는게 이상해 // 내가 여기에 보드를 그릴테니 cell 내용을 줘! + } + System.out.println(); + } + System.out.println(); + } + + public void printGameWinningComment() { + System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); + } + + public void printGameLosingComment() { + System.out.println("지뢰를 밟았습니다. GAME OVER!"); + } + + public void printCommentForSelectingCell() { + System.out.println("선택할 좌표를 입력하세요. (예: a1)"); + } + + public void printCommentFOrUserAction() { + System.out.println("선택한 셀에 대한 행위를 선택하세요. (1: 오픈, 2: 깃발 꽂기)"); + } + + public void printExceptionMessage(GameException e) { + System.out.println(e.getMessage()); + } + + public void printSimpleMessage(String message) { + System.out.println(message); + } +} From 8389e4fb2d3678fbbc2ad0b8ac4ed36e7823a89a Mon Sep 17 00:00:00 2001 From: devny Date: Thu, 6 Mar 2025 16:54:06 +0900 Subject: [PATCH 10/39] =?UTF-8?q?2025=2003=2006=20by=20devny=20-=20SRP:=20?= =?UTF-8?q?=EB=8B=A8=EC=9D=BC=20=EC=B1=85=EC=9E=84=20=EC=9B=90=EC=B9=993?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SRP에 따라서 GameBoard.java 클래스 생성 및 구현 --- .../cleancode/minesweeper/tobe/GameBoard.java | 154 ++++++++++++++++++ .../minesweeper/tobe/Minesweeper.java | 120 +------------- .../tobe/io/ConsoleOutputHandler.java | 9 +- 3 files changed, 167 insertions(+), 116 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/GameBoard.java diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java new file mode 100644 index 000000000..65a82adc7 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -0,0 +1,154 @@ +package cleancode.minesweeper.tobe; + +import java.util.Arrays; +import java.util.Random; + +public class GameBoard { + private final Cell[][] board; + public static final int LAND_MINES_COUNT = 10; + + public GameBoard(int rowSize, int colSize) { + board = new Cell[rowSize][colSize]; + } + + public void flag(int rowIndex, int colIndex) { + Cell cell = findCell(rowIndex, colIndex); + cell.flag(); + } + + public boolean isLandMineCell(int selectedRowIndex, int selectedColIndex) { + Cell cell = findCell(selectedRowIndex, selectedColIndex); + return cell.isLandMine(); + } + + public void initializeGame() { + int rowSize = board.length; + int colSize = board[0].length; + + for (int row = 0; row < rowSize; row++) { + for (int col = 0; col < colSize; col++) { + board[row][col] = Cell.create(); + } + } + + for (int i = 0; i < LAND_MINES_COUNT; i++) { // 10 이 col 숫자가아니라 지뢰숫자 + int landMineCol = new Random().nextInt(colSize); + int landMineRow = new Random().nextInt(rowSize); + findCell(landMineRow, landMineCol).turnOnLandMine(); + } + + for (int row = 0; row < rowSize; row++) { + for (int col = 0; col < colSize; col++) { + int count = 0; + if (isLandMineCell(row, col)) { + // 이미 셀의 기본속성을 0으로 바꿔서 따로 안해도 됨 + continue; + } + count = countNearbyLandMines(row, col, count); + findCell(row, col).updateNearbyLandMineCount(count); + } + } + } + + private int countNearbyLandMines(int row, int col, int count) { + int rowSize = board.length; + int colSize = board[0].length; + + if (row - 1 >= 0 && col - 1 >= 0 && isLandMineCell(row - 1, col - 1)) { + count++; + } + if (row - 1 >= 0 && isLandMineCell(row - 1, col)) { + count++; + } + if (row - 1 >= 0 && col + 1 < colSize && isLandMineCell(row - 1, col + 1)) { + count++; + } + if (col - 1 >= 0 && isLandMineCell(row, col - 1)) { + count++; + } + if (col + 1 < colSize && isLandMineCell(row, col + 1)) { + count++; + } + if (row + 1 < rowSize && col - 1 >= 0 && isLandMineCell(row + 1, col - 1)) { + count++; + } + if (row + 1 < rowSize && isLandMineCell(row + 1, col)) { + count++; + } + if (row + 1 < rowSize && col + 1 < colSize && isLandMineCell(row + 1, col + 1)) { + count++; + } + return count; + } + + public int getRowSize() { + return board.length; + } + + public boolean isAllCellChecked() { + return Arrays.stream(board)// BOARD 라는 이중 string 배열에 stream 을 걸면 String[] 형태의 Stream 이 나옴 Stream + // 그냥 Map 을 하면 Stream> 이 나오는데 flatMap 을 하면서 평탄화를 통해 이중배열을 배열로, 즉, Stream 으로 만들어주는 것 + .flatMap(stringArr -> Arrays.stream(stringArr)) // flatMap 을 하면 Stream 이 생기는데 이 stringArray 를 하나씩 돌면서 다시 Stream 만들거다 + // 여기까지가 Stream + .allMatch(Cell::isChecked); + } + + public String getSign(int rowIndex, int colIndex) { + Cell cell = findCell(rowIndex, colIndex); + return cell.getSign(); + } + + private Cell findCell(int rowIndex, int colIndex) { + return board[rowIndex][colIndex]; + } + + public int getColSize() { + return board[0].length; + } + + public void open(int rowIndex, int colIndex) { + Cell cell = findCell(rowIndex, colIndex); + cell.open(); + } + + public void openSurroundedCells(int row, int col) { + if (row < 0 || row >= getRowSize() || col < 0 || col >= getColSize()) { + return; + } + + if (isOpenedCell(row, col)) { + return; + } + + if (isLandMineCell(row, col)) { + return; + } + + // 여기까지 안열렸으면 아직 안열린 cell 이니까 열어! + open(row, col); // 오픈 + + if (doesCellHaveLandMineCount(row, col)) { // 숫자가 있으면! + // 열고 숫자를 초기화 한 것임 +// BOARD[row][col] = Cell.ofNearbyLandMineCount(NEAR_BY_LAND_MINE_COUNTS[row][col]); + return; + } + + // 재귀임 // 오픈을 시작하고 멈추는건 숫자일때, 지뢰일때임 + openSurroundedCells(row - 1, col - 1); + openSurroundedCells(row - 1, col); + openSurroundedCells(row - 1, col + 1); + openSurroundedCells(row, col - 1); + openSurroundedCells(row, col + 1); + openSurroundedCells(row + 1, col - 1); + openSurroundedCells(row + 1, col); + openSurroundedCells(row + 1, col + 1); + } + + private boolean doesCellHaveLandMineCount(int row, int col) { + return findCell(row, col).hasLandMineCount(); + } + + private boolean isOpenedCell(int row, int col) { + return findCell(row, col).isOpened(); + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index 1b6ee8e60..482f018df 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -3,18 +3,14 @@ import cleancode.minesweeper.tobe.io.ConsoleInputHandler; import cleancode.minesweeper.tobe.io.ConsoleOutputHandler; -import java.util.Arrays; -import java.util.Random; - // 모든 지뢰찾기 게임 로직을 여기에 둘 것임 public class Minesweeper { // 중요한 문자열, 숫자야. 유지보수할때 잘 봐야해! 할 수 있는 것 = 매직넘버, 매직스트링 public static final int BOARD_ROW_SIZE = 8; public static final int BOARD_COL_SIZE = 10; // 상수 컨벤션 = 대문자와 언더스코어로 이루어져 있게 해야함 - private static final Cell[][] BOARD = new Cell[BOARD_ROW_SIZE][BOARD_COL_SIZE]; - public static final int LAND_MINES_COUNT = 10; + private final GameBoard gameBoard = new GameBoard(BOARD_ROW_SIZE, BOARD_COL_SIZE); // 입출력에 대한건 여기서! private final ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(); private final ConsoleOutputHandler consoleOutputHandler = new ConsoleOutputHandler(); @@ -23,11 +19,11 @@ public class Minesweeper { public void run() { consoleOutputHandler.showGameStartCommand(); - initializeGame(); + gameBoard.initializeGame(); while (true) { try { - consoleOutputHandler.showBoard(BOARD); + consoleOutputHandler.showBoard(gameBoard); if (doesUserWinTheGame()) { consoleOutputHandler.printGameWinningComment(); @@ -57,20 +53,20 @@ private void actOnCell(String cellInput, String userActionInput) { int selectedRowIndex = getSelectedRowIndex(cellInput); if (doesUserChooseToPlantFlag(userActionInput)) { - BOARD[selectedRowIndex][selectedColIndex].flag(); + gameBoard.flag(selectedRowIndex, selectedColIndex); checkIfGameIsOver(); return; } if (doesUserChooseToOpenCell(userActionInput)) { - if (isLandMineCell(selectedRowIndex, selectedColIndex)) { - BOARD[selectedRowIndex][selectedColIndex].open(); + if (gameBoard.isLandMineCell(selectedRowIndex, selectedColIndex)) { + gameBoard.open(selectedColIndex, selectedColIndex); changeGameStatusToLose(); return; } - open(selectedRowIndex, selectedColIndex); + gameBoard.openSurroundedCells(selectedRowIndex, selectedColIndex); checkIfGameIsOver(); return; } @@ -82,10 +78,6 @@ private void changeGameStatusToLose() { gameStatus = -1; } - private boolean isLandMineCell(int selectedRowIndex, int selectedColIndex) { - return BOARD[selectedRowIndex][selectedColIndex].isLandMine(); - } - private boolean doesUserChooseToOpenCell(String userActionInput) { return userActionInput.equals("1"); } @@ -124,8 +116,7 @@ private boolean doesUserWinTheGame() { } private void checkIfGameIsOver() { - boolean isAllCellChecked = isAllCellChecked(); - if (isAllCellChecked) { // 게임 이긴 것 + if (gameBoard.isAllCellChecked()) { // 게임 이긴 것 changeGameStatusToWin(); } } @@ -146,14 +137,6 @@ private void changeGameStatusToWin() { // return isAllOpened; // } - private boolean isAllCellChecked() { - return Arrays.stream(BOARD)// BOARD 라는 이중 string 배열에 stream 을 걸면 String[] 형태의 Stream 이 나옴 Stream - // 그냥 Map 을 하면 Stream> 이 나오는데 flatMap 을 하면서 평탄화를 통해 이중배열을 배열로, 즉, Stream 으로 만들어주는 것 - .flatMap(stringArr -> Arrays.stream(stringArr)) // flatMap 을 하면 Stream 이 생기는데 이 stringArray 를 하나씩 돌면서 다시 Stream 만들거다 - // 여기까지가 Stream - .allMatch(Cell::isChecked); - } - private int convertRowFrom(char cellInputRow) { int rowIndex = Character.getNumericValue(cellInputRow) - 1; if (rowIndex > BOARD_ROW_SIZE) { @@ -191,91 +174,4 @@ private int convertColFrom(char cellInputCol) { } // return selectedColIndex; } - - private void initializeGame() { - for (int row = 0; row < BOARD_ROW_SIZE; row++) { - for (int col = 0; col < BOARD_COL_SIZE; col++) { - BOARD[row][col] = Cell.create(); - } - } - - for (int i = 0; i < LAND_MINES_COUNT; i++) { // 10 이 col 숫자가아니라 지뢰숫자 - int col = new Random().nextInt(BOARD_COL_SIZE); - int row = new Random().nextInt(BOARD_ROW_SIZE); - BOARD[row][col].turnOnLandMine(); - } - - for (int row = 0; row < BOARD_ROW_SIZE; row++) { - for (int col = 0; col < BOARD_COL_SIZE; col++) { - int count = 0; - if (isLandMineCell(row, col)) { - // 이미 셀의 기본속성을 0으로 바꿔서 따로 안해도 됨 - continue; - } - count = countNearbyLandMines(row, col, count); - BOARD[row][col].updateNearbyLandMineCount(count); - } - } - } - - private int countNearbyLandMines(int row, int col, int count) { - if (row - 1 >= 0 && col - 1 >= 0 && isLandMineCell(row - 1, col - 1)) { - count++; - } - if (row - 1 >= 0 && isLandMineCell(row - 1, col)) { - count++; - } - if (row - 1 >= 0 && col + 1 < BOARD_COL_SIZE && isLandMineCell(row - 1, col + 1)) { - count++; - } - if (col - 1 >= 0 && isLandMineCell(row, col - 1)) { - count++; - } - if (col + 1 < BOARD_COL_SIZE && isLandMineCell(row, col + 1)) { - count++; - } - if (row + 1 < BOARD_ROW_SIZE && col - 1 >= 0 && isLandMineCell(row + 1, col - 1)) { - count++; - } - if (row + 1 < BOARD_ROW_SIZE && isLandMineCell(row + 1, col)) { - count++; - } - if (row + 1 < BOARD_ROW_SIZE && col + 1 < BOARD_COL_SIZE && isLandMineCell(row + 1, col + 1)) { - count++; - } - return count; - } - - private void open(int row, int col) { - if (row < 0 || row >= BOARD_ROW_SIZE || col < 0 || col >= BOARD_COL_SIZE) { - return; - } - - if (BOARD[row][col].isOpened()) { - return; - } - - if (isLandMineCell(row, col)) { - return; - } - - // 여기까지 안열렸으면 아직 안열린 cell 이니까 열어! - BOARD[row][col].open(); // 오픈 - - if (BOARD[row][col].hasLandMineCount()) { // 숫자가 있으면! - // 열고 숫자를 초기화 한 것임 -// BOARD[row][col] = Cell.ofNearbyLandMineCount(NEAR_BY_LAND_MINE_COUNTS[row][col]); - return; - } - - // 재귀임 // 오픈을 시작하고 멈추는건 숫자일때, 지뢰일때임 - open(row - 1, col - 1); - open(row - 1, col); - open(row - 1, col + 1); - open(row, col - 1); - open(row, col + 1); - open(row + 1, col - 1); - open(row + 1, col); - open(row + 1, col + 1); - } } diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java index 4af26fda2..10df36b08 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -1,5 +1,6 @@ package cleancode.minesweeper.tobe.io; +import cleancode.minesweeper.tobe.GameBoard; import cleancode.minesweeper.tobe.GameException; import cleancode.minesweeper.tobe.Cell; @@ -10,12 +11,12 @@ public void showGameStartCommand() { System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); } - public void showBoard(Cell[][] board) { + public void showBoard(GameBoard board) { System.out.println(" a b c d e f g h i j"); - for (int row = 0; row < board.length; row++) { + for (int row = 0; row < board.getRowSize(); row++) { System.out.printf("%d ", row + 1); - for (int col = 0; col < board[0].length; col++) { - System.out.print(board[row][col].getSign() + " "); // 여기는 getter 를 안쓰는게 이상해 // 내가 여기에 보드를 그릴테니 cell 내용을 줘! + for (int col = 0; col < board.getColSize(); col++) { + System.out.print(board.getSign(row, col) + " "); // 여기는 getter 를 안쓰는게 이상해 // 내가 여기에 보드를 그릴테니 cell 내용을 줘! } System.out.println(); } From df160433933627f6b9b0ae05924b4cc6ec7bcf42 Mon Sep 17 00:00:00 2001 From: devny Date: Fri, 7 Mar 2025 09:00:44 +0900 Subject: [PATCH 11/39] =?UTF-8?q?2025=2003=2007=20by=20devny=20-=20p10=20?= =?UTF-8?q?=EA=B3=BC=20=EA=B0=99=EC=9D=B4=20=EA=B8=B0=EC=A1=B4=EC=9D=98=20?= =?UTF-8?q?row,=20col=20=EB=B2=94=EC=9C=84=EA=B0=80=20=EB=8A=98=EC=96=B4?= =?UTF-8?q?=EB=82=AC=EC=9D=84=EB=95=8C=20=EC=98=A4=EB=8F=99=EC=9E=91?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95(?= =?UTF-8?q?=EB=B2=94=EC=9C=84=20=ED=99=95=EC=9E=A5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - col: ASCII code 로 계산하도록 변경 - row: String.substring 으로 뒤에 두자리 숫자 이상도 받을 수 있도록 변경 --- .../minesweeper/tobe/Minesweeper.java | 43 +++++-------------- 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index 482f018df..4f6964d78 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -6,8 +6,9 @@ // 모든 지뢰찾기 게임 로직을 여기에 둘 것임 public class Minesweeper { // 중요한 문자열, 숫자야. 유지보수할때 잘 봐야해! 할 수 있는 것 = 매직넘버, 매직스트링 - public static final int BOARD_ROW_SIZE = 8; - public static final int BOARD_COL_SIZE = 10; + private static final int BOARD_ROW_SIZE = 8; + private static final int BOARD_COL_SIZE = 10; + private static final char BASE_CHAR_FOR_COL = 'a'; // 상수 컨벤션 = 대문자와 언더스코어로 이루어져 있게 해야함 private final GameBoard gameBoard = new GameBoard(BOARD_ROW_SIZE, BOARD_COL_SIZE); @@ -87,7 +88,7 @@ private boolean doesUserChooseToPlantFlag(String userActionInput) { } private int getSelectedRowIndex(String cellInput) { - char cellInputRow = cellInput.charAt(1); + String cellInputRow = cellInput.substring(1); return convertRowFrom(cellInputRow); } @@ -137,41 +138,19 @@ private void changeGameStatusToWin() { // return isAllOpened; // } - private int convertRowFrom(char cellInputRow) { - int rowIndex = Character.getNumericValue(cellInputRow) - 1; + private int convertRowFrom(String cellInputRow) { + int rowIndex = Integer.parseInt(cellInputRow) - 1; if (rowIndex > BOARD_ROW_SIZE) { throw new GameException("잘못된 입력입니다."); } return rowIndex; } - private int convertColFrom(char cellInputCol) { -// int selectedColIndex; - switch (cellInputCol) { - case 'a': - return 0; - case 'b': - return 1; - case 'c': - return 2; - case 'd': - return 3; - case 'e': - return 4; - case 'f': - return 5; - case 'g': - return 6; - case 'h': - return 7; - case 'i': - return 8; - case 'j': - return 9; - default: -// return -1; - throw new GameException("잘못된 입력입니다."); + private int convertColFrom(char cellInputCol) { // 'a' // 알파벳이 j 까지만 대응되어 있음 + int colIndex = cellInputCol - BASE_CHAR_FOR_COL; // a가 들어가면 0, b가 들어가면 1 + if (colIndex < 0 ) { // a = 97 인데 cellInput 이 96 이면 알파벳이 아니기 때문 // 현재 대문자는 생각하지 않음. + throw new GameException("잘못된 입력입니다."); } -// return selectedColIndex; + return colIndex; } } From 0c242d5d8503d1ee7eff6284e5335e131833c20f Mon Sep 17 00:00:00 2001 From: devny Date: Fri, 7 Mar 2025 09:17:48 +0900 Subject: [PATCH 12/39] =?UTF-8?q?2025=2003=2007=20by=20devny=20-=20SRP=20+?= =?UTF-8?q?=20OCP(=EB=8B=A8=EC=9D=BC=EC=B1=85=EC=9E=84=EC=9B=90=EC=B9=99?= =?UTF-8?q?=20+=20=EA=B0=9C=EB=B0=A9-=ED=8F=90=EC=87=84=EC=9B=90=EC=B9=99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BoardIndexConverter 하는 부분 단일책임원칙에 의해 분리 - 알파벳 j 이상에서도 가능하도록 IntStream.range 만들어주고 char 로 만들고 char -> String 만들어서 List 로 묶어준 후 join 해주기 --- .../minesweeper/tobe/BoardIndexConverter.java | 32 ++++++++++++++++ .../minesweeper/tobe/Minesweeper.java | 38 ++++--------------- .../tobe/io/ConsoleOutputHandler.java | 19 +++++++++- 3 files changed, 56 insertions(+), 33 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java diff --git a/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java b/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java new file mode 100644 index 000000000..d8aecf3fd --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java @@ -0,0 +1,32 @@ +package cleancode.minesweeper.tobe; + +public class BoardIndexConverter { + + private static final char BASE_CHAR_FOR_COL = 'a'; + + public int getSelectedRowIndex(String cellInput, int rowSize) { + String cellInputRow = cellInput.substring(1); + return convertRowFrom(cellInputRow, rowSize); + } + + public int getSelectedColIndex(String cellInput, int collSize) { + char cellInputCol = cellInput.charAt(0); + return convertColFrom(cellInputCol, collSize); + } + + private int convertRowFrom(String cellInputRow, int rowSize) { + int rowIndex = Integer.parseInt(cellInputRow) - 1; + if (rowIndex < 0 || rowIndex >= rowSize) { + throw new GameException("잘못된 입력입니다."); + } + return rowIndex; + } + + private int convertColFrom(char cellInputCol, int collSize) { // 'a' // 알파벳이 j 까지만 대응되어 있음 + int colIndex = cellInputCol - BASE_CHAR_FOR_COL; // a가 들어가면 0, b가 들어가면 1 + if (colIndex < 0 || colIndex >= collSize) { // a = 97 인데 cellInput 이 96 이면 알파벳이 아니기 때문 // 현재 대문자는 생각하지 않음. + throw new GameException("잘못된 입력입니다."); + } + return colIndex; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index 4f6964d78..8acaa214c 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -6,12 +6,14 @@ // 모든 지뢰찾기 게임 로직을 여기에 둘 것임 public class Minesweeper { // 중요한 문자열, 숫자야. 유지보수할때 잘 봐야해! 할 수 있는 것 = 매직넘버, 매직스트링 - private static final int BOARD_ROW_SIZE = 8; - private static final int BOARD_COL_SIZE = 10; - private static final char BASE_CHAR_FOR_COL = 'a'; +// private static final int BOARD_ROW_SIZE = 8; +// private static final int BOARD_COL_SIZE = 10; // 상수 컨벤션 = 대문자와 언더스코어로 이루어져 있게 해야함 + private static final int BOARD_ROW_SIZE = 14; + private static final int BOARD_COL_SIZE = 18; private final GameBoard gameBoard = new GameBoard(BOARD_ROW_SIZE, BOARD_COL_SIZE); + private final BoardIndexConverter boardIndexConverter = new BoardIndexConverter(); // 입출력에 대한건 여기서! private final ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(); private final ConsoleOutputHandler consoleOutputHandler = new ConsoleOutputHandler(); @@ -50,8 +52,8 @@ public void run() { } private void actOnCell(String cellInput, String userActionInput) { - int selectedColIndex = getSelectedColIndex(cellInput); - int selectedRowIndex = getSelectedRowIndex(cellInput); + int selectedColIndex = boardIndexConverter.getSelectedColIndex(cellInput, gameBoard.getColSize()); + int selectedRowIndex = boardIndexConverter.getSelectedRowIndex(cellInput, gameBoard.getRowSize()); if (doesUserChooseToPlantFlag(userActionInput)) { gameBoard.flag(selectedRowIndex, selectedColIndex); @@ -87,16 +89,6 @@ private boolean doesUserChooseToPlantFlag(String userActionInput) { return userActionInput.equals("2"); } - private int getSelectedRowIndex(String cellInput) { - String cellInputRow = cellInput.substring(1); - return convertRowFrom(cellInputRow); - } - - private int getSelectedColIndex(String cellInput) { - char cellInputCol = cellInput.charAt(0); - return convertColFrom(cellInputCol); - } - private String getUserActionInputFromUser() { consoleOutputHandler.printCommentFOrUserAction(); return consoleInputHandler.getUserInput(); @@ -137,20 +129,4 @@ private void changeGameStatusToWin() { // } // return isAllOpened; // } - - private int convertRowFrom(String cellInputRow) { - int rowIndex = Integer.parseInt(cellInputRow) - 1; - if (rowIndex > BOARD_ROW_SIZE) { - throw new GameException("잘못된 입력입니다."); - } - return rowIndex; - } - - private int convertColFrom(char cellInputCol) { // 'a' // 알파벳이 j 까지만 대응되어 있음 - int colIndex = cellInputCol - BASE_CHAR_FOR_COL; // a가 들어가면 0, b가 들어가면 1 - if (colIndex < 0 ) { // a = 97 인데 cellInput 이 96 이면 알파벳이 아니기 때문 // 현재 대문자는 생각하지 않음. - throw new GameException("잘못된 입력입니다."); - } - return colIndex; - } } diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java index 10df36b08..8f9891d28 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -4,6 +4,9 @@ import cleancode.minesweeper.tobe.GameException; import cleancode.minesweeper.tobe.Cell; +import java.util.List; +import java.util.stream.IntStream; + public class ConsoleOutputHandler { public void showGameStartCommand() { System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); @@ -12,9 +15,12 @@ public void showGameStartCommand() { } public void showBoard(GameBoard board) { - System.out.println(" a b c d e f g h i j"); + String alphabets = generateColAlphabets(board); + + System.out.println(" " + alphabets); + for (int row = 0; row < board.getRowSize(); row++) { - System.out.printf("%d ", row + 1); + System.out.printf("%2d ", row + 1); // 2자리 수 이상일때 col 정렬 맞출 수 있도록 함 for (int col = 0; col < board.getColSize(); col++) { System.out.print(board.getSign(row, col) + " "); // 여기는 getter 를 안쓰는게 이상해 // 내가 여기에 보드를 그릴테니 cell 내용을 줘! } @@ -23,6 +29,15 @@ public void showBoard(GameBoard board) { System.out.println(); } + private static String generateColAlphabets(GameBoard board) { + List alphabets = IntStream.range(0, board.getColSize()) // 0 부터 colSize 까지 range 만듦 + .mapToObj(index -> (char)('a' + index)) // 'a' + 0 = 'a' + .map(c -> c.toString()) + .toList(); // 알파벳 묶음이 될 것임 + String joiningAlphabets = String.join(" ", alphabets); // 알파벳들을 연결해줘 + return joiningAlphabets; + } + public void printGameWinningComment() { System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); } From 7e293f292f4bb8b3d060699e8f26558c8ef75728 Mon Sep 17 00:00:00 2001 From: devny Date: Mon, 10 Mar 2025 12:13:36 +0900 Subject: [PATCH 13/39] =?UTF-8?q?2025=2003=2007=20by=20devny=20-=20OCP(?= =?UTF-8?q?=EA=B0=9C=EB=B0=A9-=ED=8F=90=EC=87=84=EC=9B=90=EC=B9=99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 게임 난이도를 인터페이스로 생성 + 난이도 조정시 구현체로 난이도가 변경된 구현체를 넣어줄 것 + GameBoard.java 생성시 인터페이스를 주입받아 row, col, landminecount 받을 수 있게 하여 게임을 유동적으로 할 수 있게 함 = OCP 만족 --- .../minesweeper/tobe/GameApplication.java | 8 +++++++- .../cleancode/minesweeper/tobe/GameBoard.java | 13 ++++++++++--- .../minesweeper/tobe/Minesweeper.java | 10 +++++++--- .../minesweeper/tobe/gamelevel/Advanced.java | 18 ++++++++++++++++++ .../minesweeper/tobe/gamelevel/Beginner.java | 18 ++++++++++++++++++ .../minesweeper/tobe/gamelevel/GameLevel.java | 9 +++++++++ .../minesweeper/tobe/gamelevel/Middle.java | 18 ++++++++++++++++++ .../tobe/gamelevel/VeryBeginner.java | 18 ++++++++++++++++++ .../tobe/io/ConsoleOutputHandler.java | 1 - 9 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/gamelevel/Advanced.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/gamelevel/Beginner.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/gamelevel/GameLevel.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/gamelevel/Middle.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/gamelevel/VeryBeginner.java diff --git a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java index a4cf65a4d..49a04777f 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java @@ -1,9 +1,15 @@ package cleancode.minesweeper.tobe; +import cleancode.minesweeper.tobe.gamelevel.Advanced; +import cleancode.minesweeper.tobe.gamelevel.Beginner; +import cleancode.minesweeper.tobe.gamelevel.GameLevel; + public class GameApplication { public static void main(String[] args) { - Minesweeper minesweeper = new Minesweeper(); + GameLevel gameLevel = new Advanced(); + + Minesweeper minesweeper = new Minesweeper(gameLevel); minesweeper.run(); } } \ No newline at end of file diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index 65a82adc7..de76892b8 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -1,14 +1,21 @@ package cleancode.minesweeper.tobe; +import cleancode.minesweeper.tobe.gamelevel.GameLevel; + import java.util.Arrays; import java.util.Random; public class GameBoard { private final Cell[][] board; - public static final int LAND_MINES_COUNT = 10; + private final int landMineCount; + + public GameBoard(GameLevel gameLevel) { + int rowSize = gameLevel.getRowSize(); + int colSize = gameLevel.getColSize(); - public GameBoard(int rowSize, int colSize) { board = new Cell[rowSize][colSize]; + + landMineCount = gameLevel.getLandMineCount(); } public void flag(int rowIndex, int colIndex) { @@ -31,7 +38,7 @@ public void initializeGame() { } } - for (int i = 0; i < LAND_MINES_COUNT; i++) { // 10 이 col 숫자가아니라 지뢰숫자 + for (int i = 0; i < landMineCount; i++) { // 10 이 col 숫자가아니라 지뢰숫자 int landMineCol = new Random().nextInt(colSize); int landMineRow = new Random().nextInt(rowSize); findCell(landMineRow, landMineCol).turnOnLandMine(); diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index 8acaa214c..189d76b40 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -1,5 +1,6 @@ package cleancode.minesweeper.tobe; +import cleancode.minesweeper.tobe.gamelevel.GameLevel; import cleancode.minesweeper.tobe.io.ConsoleInputHandler; import cleancode.minesweeper.tobe.io.ConsoleOutputHandler; @@ -9,10 +10,9 @@ public class Minesweeper { // private static final int BOARD_ROW_SIZE = 8; // private static final int BOARD_COL_SIZE = 10; // 상수 컨벤션 = 대문자와 언더스코어로 이루어져 있게 해야함 - private static final int BOARD_ROW_SIZE = 14; - private static final int BOARD_COL_SIZE = 18; - private final GameBoard gameBoard = new GameBoard(BOARD_ROW_SIZE, BOARD_COL_SIZE); + + private final GameBoard gameBoard; private final BoardIndexConverter boardIndexConverter = new BoardIndexConverter(); // 입출력에 대한건 여기서! private final ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(); @@ -20,6 +20,10 @@ public class Minesweeper { private int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 + public Minesweeper(GameLevel gameLevel) { + gameBoard = new GameBoard(gameLevel); + } + public void run() { consoleOutputHandler.showGameStartCommand(); gameBoard.initializeGame(); diff --git a/src/main/java/cleancode/minesweeper/tobe/gamelevel/Advanced.java b/src/main/java/cleancode/minesweeper/tobe/gamelevel/Advanced.java new file mode 100644 index 000000000..4d378572d --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/gamelevel/Advanced.java @@ -0,0 +1,18 @@ +package cleancode.minesweeper.tobe.gamelevel; + +public class Advanced implements GameLevel { // GameLevel(추상화된 클래스) 의 Spec 을 만족시키는 구현체 + @Override + public int getRowSize() { + return 20; + } + + @Override + public int getColSize() { + return 24; + } + + @Override + public int getLandMineCount() { + return 99; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/gamelevel/Beginner.java b/src/main/java/cleancode/minesweeper/tobe/gamelevel/Beginner.java new file mode 100644 index 000000000..623531557 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/gamelevel/Beginner.java @@ -0,0 +1,18 @@ +package cleancode.minesweeper.tobe.gamelevel; + +public class Beginner implements GameLevel { // GameLevel(추상화된 클래스) 의 Spec 을 만족시키는 구현체 + @Override + public int getRowSize() { + return 8; + } + + @Override + public int getColSize() { + return 10; + } + + @Override + public int getLandMineCount() { + return 10; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/gamelevel/GameLevel.java b/src/main/java/cleancode/minesweeper/tobe/gamelevel/GameLevel.java new file mode 100644 index 000000000..ef4d7121b --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/gamelevel/GameLevel.java @@ -0,0 +1,9 @@ +package cleancode.minesweeper.tobe.gamelevel; + +public interface GameLevel { + int getRowSize(); + + int getColSize(); + + int getLandMineCount(); +} diff --git a/src/main/java/cleancode/minesweeper/tobe/gamelevel/Middle.java b/src/main/java/cleancode/minesweeper/tobe/gamelevel/Middle.java new file mode 100644 index 000000000..2a4217167 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/gamelevel/Middle.java @@ -0,0 +1,18 @@ +package cleancode.minesweeper.tobe.gamelevel; + +public class Middle implements GameLevel { // GameLevel(추상화된 클래스) 의 Spec 을 만족시키는 구현체 + @Override + public int getRowSize() { + return 14; + } + + @Override + public int getColSize() { + return 18; + } + + @Override + public int getLandMineCount() { + return 40; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/gamelevel/VeryBeginner.java b/src/main/java/cleancode/minesweeper/tobe/gamelevel/VeryBeginner.java new file mode 100644 index 000000000..470f697b8 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/gamelevel/VeryBeginner.java @@ -0,0 +1,18 @@ +package cleancode.minesweeper.tobe.gamelevel; + +public class VeryBeginner implements GameLevel { // GameLevel(추상화된 클래스) 의 Spec 을 만족시키는 구현체 + @Override + public int getRowSize() { + return 4; + } + + @Override + public int getColSize() { + return 5; + } + + @Override + public int getLandMineCount() { + return 2; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java index 8f9891d28..c96d79691 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -2,7 +2,6 @@ import cleancode.minesweeper.tobe.GameBoard; import cleancode.minesweeper.tobe.GameException; -import cleancode.minesweeper.tobe.Cell; import java.util.List; import java.util.stream.IntStream; From 076f6eb4d4ddd0e4ccc449fd6161cc2693b0b6dd Mon Sep 17 00:00:00 2001 From: devny Date: Mon, 10 Mar 2025 14:57:33 +0900 Subject: [PATCH 14/39] =?UTF-8?q?2025=2003=2010=20by=20devny=20-=20LSP(?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=EC=BD=94=ED=94=84=20=EC=B9=98=ED=99=98=20?= =?UTF-8?q?=EC=9B=90=EC=B9=99)=20+=20Cell2=20=EB=A5=BC=20=EC=83=81?= =?UTF-8?q?=EC=86=8D=EA=B5=AC=EC=A1=B0=EB=A1=9C=20=EB=A7=8C=EB=93=A4?= =?UTF-8?q?=EA=B3=A0=20+=20turnOnLandMine=20=EA=B3=BC=20=EA=B0=99=EC=9D=80?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20=EB=B6=80=EB=AA=A8=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=84=A0=EC=96=B8=ED=95=98=EB=A9=B4=20=EC=9E=90?= =?UTF-8?q?=EC=8B=9D=EB=93=A4=20=EC=A4=91=EC=97=90=EC=84=9C=EB=8A=94=20?= =?UTF-8?q?=ED=95=84=EC=9A=94=EC=97=86=EB=8A=94=20=EC=9E=90=EC=8B=9D?= =?UTF-8?q?=EC=9D=B4=20=EC=9E=88=EC=9D=8C.=20=EC=9D=B4=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=EC=97=90=EB=8A=94=20throw=20UnSupported..Exception=20?= =?UTF-8?q?=EC=9D=84=20=EB=8D=98=EC=A7=90=20->=20=EA=B7=B8=EB=9F=BC=20?= =?UTF-8?q?=EB=8B=A4=EB=A5=B8=20=EA=B3=B3=EC=97=90=EC=84=9C=20=EC=93=B8?= =?UTF-8?q?=EB=95=8C=20=EC=A7=80=EC=9B=90=ED=95=98=EB=8A=94=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=EC=9D=B8=EC=A7=80=20=ED=99=95=EC=9D=B8=EC=9D=84=20?= =?UTF-8?q?=ED=95=98=EA=B3=A0=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=B4=20=EB=93=A4=EC=96=B4=EA=B0=90=20->?= =?UTF-8?q?=20=EC=A2=8B=EC=A7=80=20=EC=95=8A=EC=9D=8C(LSP=20=EC=A4=80?= =?UTF-8?q?=EC=88=98=ED=95=98=EC=A7=80=20=EC=95=8A=EC=9D=8C)=20+=20?= =?UTF-8?q?=EB=8B=A4=EC=8B=9C=20LSP=EC=97=90=20=EB=A7=9E=EB=8A=94=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=A8.=20?= =?UTF-8?q?=ED=98=84=EC=9E=AC=20=EA=B2=B0=EA=B3=BC=EB=8A=94=20LSP=20?= =?UTF-8?q?=EB=A5=BC=20=EC=9E=98=20=EB=A7=8C=EC=A1=B1=ED=95=9C=20=EC=83=81?= =?UTF-8?q?=EC=86=8D=EA=B5=AC=EC=A1=B0=EA=B0=80=20=EB=90=90=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cleancode/minesweeper/tobe/Cell.java | 87 ------------------- .../minesweeper/tobe/GameApplication.java | 2 +- .../cleancode/minesweeper/tobe/GameBoard.java | 18 ++-- .../cleancode/minesweeper/tobe/cell/Cell.java | 36 ++++++++ .../minesweeper/tobe/cell/EmptyCell.java | 26 ++++++ .../minesweeper/tobe/cell/LandMineCell.java | 26 ++++++ .../minesweeper/tobe/cell/NumberCell.java | 30 +++++++ 7 files changed, 132 insertions(+), 93 deletions(-) delete mode 100644 src/main/java/cleancode/minesweeper/tobe/Cell.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/cell/Cell.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java diff --git a/src/main/java/cleancode/minesweeper/tobe/Cell.java b/src/main/java/cleancode/minesweeper/tobe/Cell.java deleted file mode 100644 index 43bfe1515..000000000 --- a/src/main/java/cleancode/minesweeper/tobe/Cell.java +++ /dev/null @@ -1,87 +0,0 @@ -package cleancode.minesweeper.tobe; - -public class Cell { - private static final String FLAG_SIGN = "⚑"; - private static final String LAND_MINE_SIGN = "☼"; - private static final String UNCHECKED_SIGN = "□"; - private static final String EMPTY_SIGN = "■"; - - // 보드를 기반으로 먼저 만들어 보겠다. - // final 인 데이터는 최대한 final 로 만들어야 함 - private int nearbyLandMineCount; // 주변 지뢰 수 - private boolean isLandMine; // 지뢰여부 - private boolean isFlagged; - private boolean isOpened; - // Cell 이 가진 속성: 근처 지뢰 숫자, 지뢰 여부 - // Cell의 상태: 깃발 유무, 열렸다/닫혔다, 사용자가 확인함 - - private Cell(int nearbyLandMineCount, boolean isLandMine, boolean isFlagged, boolean isOpened) { - this.nearbyLandMineCount = nearbyLandMineCount; - this.isLandMine = isLandMine; - this.isFlagged = isFlagged; - this.isOpened = isOpened; - } - - // new Cell 보다 정적팩토리 메서드를 즐겨씀 why? 이름을 부여해줄 수 있기 때문에 - // 그리고 생성자는 private 으로 변경 - // 결과: 정적패토리 메서드가 생성자 역할을 할 수 있도록 함 - public static Cell of(int nearbyLandMineCount, boolean isLandMine, boolean isFlagged, boolean isOpened) { - return new Cell(nearbyLandMineCount, isLandMine, isFlagged, isOpened); - } - - public static Cell create() { - return of(0, false, false, false); - } - - public void turnOnLandMine() { - this.isLandMine = true; - } - - public void flag() { - this.isFlagged = true; - } - - public void open() { - this.isOpened = true; - } - - public boolean isChecked() { - return isFlagged || isOpened; // 닫혀있는데 깃발이 꽂혀있거나, 열었거나 - } - - public boolean isLandMine() { - return isLandMine; - } - - public boolean isOpened() { - return isOpened; - } - - public boolean hasLandMineCount() { - return this.nearbyLandMineCount != 0; - } - - public void updateNearbyLandMineCount(int count) { - this.nearbyLandMineCount = count; - } - - public String getSign() { - if (isOpened) { - if (isLandMine) { - return LAND_MINE_SIGN; - } - - if (hasLandMineCount()) { - return String.valueOf(nearbyLandMineCount); - } - - return EMPTY_SIGN; - } - - if (isFlagged) { - return FLAG_SIGN; - } - - return UNCHECKED_SIGN; - } -} diff --git a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java index 49a04777f..751b1a64a 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java @@ -7,7 +7,7 @@ public class GameApplication { public static void main(String[] args) { - GameLevel gameLevel = new Advanced(); + GameLevel gameLevel = new Beginner(); Minesweeper minesweeper = new Minesweeper(gameLevel); minesweeper.run(); diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index de76892b8..1f97dc65d 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -1,5 +1,9 @@ package cleancode.minesweeper.tobe; +import cleancode.minesweeper.tobe.cell.Cell; +import cleancode.minesweeper.tobe.cell.EmptyCell; +import cleancode.minesweeper.tobe.cell.LandMineCell; +import cleancode.minesweeper.tobe.cell.NumberCell; import cleancode.minesweeper.tobe.gamelevel.GameLevel; import java.util.Arrays; @@ -29,19 +33,20 @@ public boolean isLandMineCell(int selectedRowIndex, int selectedColIndex) { } public void initializeGame() { - int rowSize = board.length; - int colSize = board[0].length; + int rowSize = getRowSize(); + int colSize = getColSize(); for (int row = 0; row < rowSize; row++) { for (int col = 0; col < colSize; col++) { - board[row][col] = Cell.create(); + board[row][col] = new EmptyCell(); } } for (int i = 0; i < landMineCount; i++) { // 10 이 col 숫자가아니라 지뢰숫자 int landMineCol = new Random().nextInt(colSize); int landMineRow = new Random().nextInt(rowSize); - findCell(landMineRow, landMineCol).turnOnLandMine(); + board[landMineRow][landMineCol] = new LandMineCell(); +// findCell(landMineRow, landMineCol).turnOnLandMine(); } for (int row = 0; row < rowSize; row++) { @@ -52,7 +57,10 @@ public void initializeGame() { continue; } count = countNearbyLandMines(row, col, count); - findCell(row, col).updateNearbyLandMineCount(count); + if (count == 0) // 기존에는 Cell 하나여서 괜찮았지만 이제 NumberCell 이 생겼으니 count == 0 일땐 NumberCell 을 생성하지 않도록 해야함 + continue; + board[row][col] = new NumberCell(count);; +// findCell(row, col).updateNearbyLandMineCount(count); } } } diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java b/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java new file mode 100644 index 000000000..a36c2c68c --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java @@ -0,0 +1,36 @@ +package cleancode.minesweeper.tobe.cell; + +public abstract class Cell { + protected static final String FLAG_SIGN = "⚑"; + protected static final String UNCHECKED_SIGN = "□"; + + // 보드를 기반으로 먼저 만들어 보겠다. + // final 인 데이터는 최대한 final 로 만들어야 함 + protected boolean isFlagged; + protected boolean isOpened; + // Cell 이 가진 속성: 근처 지뢰 숫자, 지뢰 여부 + // Cell의 상태: 깃발 유무, 열렸다/닫혔다, 사용자가 확인함 + + public abstract boolean isLandMine(); + + public abstract boolean hasLandMineCount(); + + public abstract String getSign(); + + // flag, open, isChecked 는 다 공통일 것 같음 + public void flag() { + this.isFlagged = true; + } + + public void open() { + this.isOpened = true; + } + + public boolean isChecked() { + return isFlagged || isOpened; // 닫혀있는데 깃발이 꽂혀있거나, 열었거나 + } + + public boolean isOpened() { + return isOpened; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java new file mode 100644 index 000000000..8b85ec01d --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java @@ -0,0 +1,26 @@ +package cleancode.minesweeper.tobe.cell; + +public class EmptyCell extends Cell { + protected static final String EMPTY_SIGN = "■"; + + @Override + public boolean isLandMine() { + return false; + } + + @Override + public boolean hasLandMineCount() { + return false; + } + + @Override + public String getSign() { + if (isOpened) { + return EMPTY_SIGN; + } + if (isFlagged) { + return FLAG_SIGN; + } + return UNCHECKED_SIGN; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java new file mode 100644 index 000000000..f7d48d6d4 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java @@ -0,0 +1,26 @@ +package cleancode.minesweeper.tobe.cell; + +public class LandMineCell extends Cell { + private static final String LAND_MINE_SIGN = "☼"; + + @Override + public boolean isLandMine() { + return true; + } + + @Override + public boolean hasLandMineCount() { + return false; + } + + @Override + public String getSign() { + if (isOpened) { + return LAND_MINE_SIGN; + } + if (isFlagged) { + return FLAG_SIGN; + } + return UNCHECKED_SIGN; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java new file mode 100644 index 000000000..84e9e90a2 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java @@ -0,0 +1,30 @@ +package cleancode.minesweeper.tobe.cell; + +public class NumberCell extends Cell { + private final int nearbyLandMineCount; // 주변 지뢰 수 + + public NumberCell(int nearbyLandMineCount) { + this.nearbyLandMineCount = nearbyLandMineCount; + } + + @Override + public boolean isLandMine() { + return false; + } + + @Override + public boolean hasLandMineCount() { + return true; + } + + @Override + public String getSign() { + if (isOpened) { + return String.valueOf(nearbyLandMineCount); + } + if (isFlagged) { + return FLAG_SIGN; + } + return UNCHECKED_SIGN; + } +} From a4404219c0bc31a4024c8d2b9aecd509ea46ca88 Mon Sep 17 00:00:00 2001 From: devny Date: Mon, 10 Mar 2025 15:08:10 +0900 Subject: [PATCH 15/39] =?UTF-8?q?2025=2003=2010=20by=20devny=20-=20ISP(?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20=EC=9B=90=EC=B9=99)=5F1=20-=20Game=20Interface=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20-=20Minesweeper=20Game=20Interface=20?= =?UTF-8?q?=EC=83=81=EC=86=8D=20=EB=B0=9B=EB=8F=84=EB=A1=9D=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/cleancode/minesweeper/tobe/Game.java | 8 ++++++++ .../java/cleancode/minesweeper/tobe/GameApplication.java | 1 + .../java/cleancode/minesweeper/tobe/Minesweeper.java | 9 +++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/Game.java diff --git a/src/main/java/cleancode/minesweeper/tobe/Game.java b/src/main/java/cleancode/minesweeper/tobe/Game.java new file mode 100644 index 000000000..dc573f10c --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/Game.java @@ -0,0 +1,8 @@ +package cleancode.minesweeper.tobe; + +public interface Game { + + void initialize(); + + void run(); +} diff --git a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java index 751b1a64a..bca4bc75b 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java @@ -10,6 +10,7 @@ public static void main(String[] args) { GameLevel gameLevel = new Beginner(); Minesweeper minesweeper = new Minesweeper(gameLevel); + minesweeper.initialize(); minesweeper.run(); } } \ No newline at end of file diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index 189d76b40..5888f1985 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -5,7 +5,7 @@ import cleancode.minesweeper.tobe.io.ConsoleOutputHandler; // 모든 지뢰찾기 게임 로직을 여기에 둘 것임 -public class Minesweeper { +public class Minesweeper implements Game { // 중요한 문자열, 숫자야. 유지보수할때 잘 봐야해! 할 수 있는 것 = 매직넘버, 매직스트링 // private static final int BOARD_ROW_SIZE = 8; // private static final int BOARD_COL_SIZE = 10; @@ -24,9 +24,14 @@ public Minesweeper(GameLevel gameLevel) { gameBoard = new GameBoard(gameLevel); } + @Override + public void initialize() { + gameBoard.initializeGame(); + } + + @Override public void run() { consoleOutputHandler.showGameStartCommand(); - gameBoard.initializeGame(); while (true) { try { From 8864b7428d72e12adc4c55f9f4c68d40416bdc13 Mon Sep 17 00:00:00 2001 From: devny Date: Mon, 10 Mar 2025 15:11:14 +0900 Subject: [PATCH 16/39] =?UTF-8?q?2025=2003=2010=20by=20devny=20-=20ISP(?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20=EC=9B=90=EC=B9=99)=5F2=20-=20AnotherGame=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80(initialize=20=EA=B0=80=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=20=EC=97=86=EC=9D=8C)=20=20=20+=20=EC=9D=B4=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20Game=ED=81=B4=EB=9E=98=EC=8A=A4=EC=97=90=EC=84=9C?= =?UTF-8?q?=20initialize=20=EC=84=A0=EC=96=B8=EB=B6=80=EA=B0=80=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EB=90=98=EB=A9=B4=20AnotherGame=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=EC=97=90=EC=84=9C=EB=8F=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=9D=84=20=ED=95=B4=EC=A3=BC=EC=96=B4=EC=95=BC=20?= =?UTF-8?q?=EB=90=A8.=20=EA=B0=99=EC=9D=B4=20=EC=88=98=EC=A0=95=EC=9D=B4?= =?UTF-8?q?=20=EB=90=98=EC=96=B4=EC=95=BC=20=ED=95=98=EA=B8=B0=20=EB=95=8C?= =?UTF-8?q?=EB=AC=B8=EC=97=90=20=EB=AC=B8=EC=A0=9C=20=EC=A0=90=EC=9D=B4=20?= =?UTF-8?q?=EC=9E=88=EC=9D=8C.=20->=20ISP=20=EC=9B=90=EC=B9=99=EC=97=90=20?= =?UTF-8?q?=EC=9C=84=EB=B0=B0!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleancode/minesweeper/tobe/AnotherGame.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/java/cleancode/minesweeper/tobe/AnotherGame.java diff --git a/src/main/java/cleancode/minesweeper/tobe/AnotherGame.java b/src/main/java/cleancode/minesweeper/tobe/AnotherGame.java new file mode 100644 index 000000000..d80eab669 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/AnotherGame.java @@ -0,0 +1,13 @@ +package cleancode.minesweeper.tobe; + +public class AnotherGame implements Game { + @Override + public void initialize() { + // ...필요없는데... + } + + @Override + public void run() { + // 게임 진행 + } +} From 6ca6d2f8e505a572d70bb2a28e4d17f7d3c8e8bc Mon Sep 17 00:00:00 2001 From: devny Date: Mon, 10 Mar 2025 15:17:14 +0900 Subject: [PATCH 17/39] =?UTF-8?q?2025=2003=2010=20by=20devny=20-=20ISP(?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20=EC=9B=90=EC=B9=99)=5F2=20-=20Interface=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC!=20GameInitializable,=20GameRunnable=20-=20AnotherGam?= =?UTF-8?q?e=20=EC=9D=80=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20initialize()=20=ED=95=A8=EC=88=98=EB=8A=94?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0=ED=95=98=EA=B3=A0=20GameRunnable=20?= =?UTF-8?q?=EB=A7=8C=20=EB=B0=9B=EC=95=84=EC=84=9C=20run=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EB=A7=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cleancode/minesweeper/tobe/AnotherGame.java | 12 +++++++----- src/main/java/cleancode/minesweeper/tobe/Game.java | 8 -------- .../java/cleancode/minesweeper/tobe/Minesweeper.java | 4 +++- .../minesweeper/tobe/game/GameInitializable.java | 6 ++++++ .../minesweeper/tobe/game/GameRunnable.java | 5 +++++ 5 files changed, 21 insertions(+), 14 deletions(-) delete mode 100644 src/main/java/cleancode/minesweeper/tobe/Game.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/game/GameInitializable.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/game/GameRunnable.java diff --git a/src/main/java/cleancode/minesweeper/tobe/AnotherGame.java b/src/main/java/cleancode/minesweeper/tobe/AnotherGame.java index d80eab669..64290246d 100644 --- a/src/main/java/cleancode/minesweeper/tobe/AnotherGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/AnotherGame.java @@ -1,10 +1,12 @@ package cleancode.minesweeper.tobe; -public class AnotherGame implements Game { - @Override - public void initialize() { - // ...필요없는데... - } +import cleancode.minesweeper.tobe.game.GameRunnable; + +public class AnotherGame implements GameRunnable { +// @Override +// public void initialize() { +// // ...필요없는데... +// } @Override public void run() { diff --git a/src/main/java/cleancode/minesweeper/tobe/Game.java b/src/main/java/cleancode/minesweeper/tobe/Game.java deleted file mode 100644 index dc573f10c..000000000 --- a/src/main/java/cleancode/minesweeper/tobe/Game.java +++ /dev/null @@ -1,8 +0,0 @@ -package cleancode.minesweeper.tobe; - -public interface Game { - - void initialize(); - - void run(); -} diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index 5888f1985..658ec04c8 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -1,11 +1,13 @@ package cleancode.minesweeper.tobe; +import cleancode.minesweeper.tobe.game.GameInitializable; +import cleancode.minesweeper.tobe.game.GameRunnable; import cleancode.minesweeper.tobe.gamelevel.GameLevel; import cleancode.minesweeper.tobe.io.ConsoleInputHandler; import cleancode.minesweeper.tobe.io.ConsoleOutputHandler; // 모든 지뢰찾기 게임 로직을 여기에 둘 것임 -public class Minesweeper implements Game { +public class Minesweeper implements GameInitializable, GameRunnable { // 중요한 문자열, 숫자야. 유지보수할때 잘 봐야해! 할 수 있는 것 = 매직넘버, 매직스트링 // private static final int BOARD_ROW_SIZE = 8; // private static final int BOARD_COL_SIZE = 10; diff --git a/src/main/java/cleancode/minesweeper/tobe/game/GameInitializable.java b/src/main/java/cleancode/minesweeper/tobe/game/GameInitializable.java new file mode 100644 index 000000000..e64ee1f11 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/game/GameInitializable.java @@ -0,0 +1,6 @@ +package cleancode.minesweeper.tobe.game; + +public interface GameInitializable { + + void initialize(); +} diff --git a/src/main/java/cleancode/minesweeper/tobe/game/GameRunnable.java b/src/main/java/cleancode/minesweeper/tobe/game/GameRunnable.java new file mode 100644 index 000000000..ab1ef2c93 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/game/GameRunnable.java @@ -0,0 +1,5 @@ +package cleancode.minesweeper.tobe.game; + +public interface GameRunnable { + void run(); +} From 3d874f332570d51f63c74703bd11d286690f1740 Mon Sep 17 00:00:00 2001 From: devny Date: Mon, 10 Mar 2025 16:08:28 +0900 Subject: [PATCH 18/39] =?UTF-8?q?2025=2003=2010=20by=20devny=20-=20DIP(?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=97=AD=EC=A0=84=20=EC=9B=90?= =?UTF-8?q?=EC=B9=99)=20-=20InputHandler.java,=20OutputHandler.java=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20-=20Minesweeper.java=20=EB=8A=94=20InputHa?= =?UTF-8?q?ndler.java=20=EC=99=80=20OutputHandler.java=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=EB=A7=8C=20=EC=95=8C?= =?UTF-8?q?=EA=B3=A0=EC=9E=88=EA=B3=A0=20=EC=BD=98=EC=86=94=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=EA=B1=B4=20=EB=AA=A8=EB=A5=B4=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=ED=95=A8.=20-=20=EA=B3=A0=EC=88=98=EC=A4=80=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88(Minesweeper)=20=EC=9D=B4=20=EC=B6=94?= =?UTF-8?q?=EC=83=81=ED=99=94(InputHandler,=20OutputHandler)=20=EC=97=90?= =?UTF-8?q?=EB=A7=8C=20=EC=9D=98=EC=A1=B4=ED=95=98=EA=B3=A0=20=EC=9E=88?= =?UTF-8?q?=EC=9D=8C.=20-=20=EC=9B=90=EB=9E=98=EB=8A=94=20=EA=B3=A0?= =?UTF-8?q?=EC=88=98=EC=A4=80=20=EB=AA=A8=EB=93=88=EC=9D=B4=20=EC=A0=80?= =?UTF-8?q?=EC=88=98=EC=A4=80=20=EB=AA=A8=EB=93=88(ConsoleOut/InputHandler?= =?UTF-8?q?)=20=EB=A5=BC=20=EC=B0=B8=EC=A1=B0=ED=96=88=EC=A7=80=EB=A7=8C?= =?UTF-8?q?=20=EC=B6=94=EC=83=81=ED=99=94=EC=97=90=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=ED=95=A8!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/GameApplication.java | 8 ++++- .../minesweeper/tobe/Minesweeper.java | 30 +++++++++++-------- .../tobe/io/ConsoleInputHandler.java | 3 +- .../tobe/io/ConsoleOutputHandler.java | 10 ++++++- .../minesweeper/tobe/io/InputHandler.java | 5 ++++ .../minesweeper/tobe/io/OutputHandler.java | 26 ++++++++++++++++ 6 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java diff --git a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java index bca4bc75b..eea1dec21 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java @@ -3,13 +3,19 @@ import cleancode.minesweeper.tobe.gamelevel.Advanced; import cleancode.minesweeper.tobe.gamelevel.Beginner; import cleancode.minesweeper.tobe.gamelevel.GameLevel; +import cleancode.minesweeper.tobe.io.ConsoleInputHandler; +import cleancode.minesweeper.tobe.io.ConsoleOutputHandler; +import cleancode.minesweeper.tobe.io.InputHandler; +import cleancode.minesweeper.tobe.io.OutputHandler; public class GameApplication { public static void main(String[] args) { GameLevel gameLevel = new Beginner(); + InputHandler inputHandler = new ConsoleInputHandler(); + OutputHandler outputHandler = new ConsoleOutputHandler(); - Minesweeper minesweeper = new Minesweeper(gameLevel); + Minesweeper minesweeper = new Minesweeper(gameLevel, inputHandler, outputHandler); minesweeper.initialize(); minesweeper.run(); } diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index 658ec04c8..a42af4cff 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -5,6 +5,8 @@ import cleancode.minesweeper.tobe.gamelevel.GameLevel; import cleancode.minesweeper.tobe.io.ConsoleInputHandler; import cleancode.minesweeper.tobe.io.ConsoleOutputHandler; +import cleancode.minesweeper.tobe.io.InputHandler; +import cleancode.minesweeper.tobe.io.OutputHandler; // 모든 지뢰찾기 게임 로직을 여기에 둘 것임 public class Minesweeper implements GameInitializable, GameRunnable { @@ -17,13 +19,15 @@ public class Minesweeper implements GameInitializable, GameRunnable { private final GameBoard gameBoard; private final BoardIndexConverter boardIndexConverter = new BoardIndexConverter(); // 입출력에 대한건 여기서! - private final ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(); - private final ConsoleOutputHandler consoleOutputHandler = new ConsoleOutputHandler(); + private final InputHandler inputHandler; + private final OutputHandler outputHandler; private int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 - public Minesweeper(GameLevel gameLevel) { + public Minesweeper(GameLevel gameLevel, InputHandler inputHandler, OutputHandler outputHandler) { gameBoard = new GameBoard(gameLevel); + this.inputHandler = inputHandler; + this.outputHandler = outputHandler; } @Override @@ -33,18 +37,18 @@ public void initialize() { @Override public void run() { - consoleOutputHandler.showGameStartCommand(); + outputHandler.showGameStartCommand(); while (true) { try { - consoleOutputHandler.showBoard(gameBoard); + outputHandler.showBoard(gameBoard); if (doesUserWinTheGame()) { - consoleOutputHandler.printGameWinningComment(); + outputHandler.printGameWinningComment(); break; } if (doesUserLoseTheGame()) { - consoleOutputHandler.printGameLosingComment(); + outputHandler.printGameLosingComment(); break; } @@ -53,10 +57,10 @@ public void run() { actOnCell(cellInput, userActionInput); } catch (GameException e) { // 의도적인 Exception - consoleOutputHandler.printExceptionMessage(e); + outputHandler.printExceptionMessage(e); } catch (Exception e) { // 예상하지 못한 Exception - consoleOutputHandler.printSimpleMessage("프로그램에 문제가 생겼습니다."); + outputHandler.printSimpleMessage("프로그램에 문제가 생겼습니다."); // e.printStackTrace(); // 실무에서는 Antipattern 실무에서는 log 시스템에서 log 를 남기고 별도의 조치를 취함 } } @@ -101,14 +105,14 @@ private boolean doesUserChooseToPlantFlag(String userActionInput) { } private String getUserActionInputFromUser() { - consoleOutputHandler.printCommentFOrUserAction(); - return consoleInputHandler.getUserInput(); + outputHandler.printCommentFOrUserAction(); + return inputHandler.getUserInput(); } private String getCellInputFromUser() { - consoleOutputHandler.printCommentForSelectingCell(); + outputHandler.printCommentForSelectingCell(); - return consoleInputHandler.getUserInput(); + return inputHandler.getUserInput(); } private boolean doesUserLoseTheGame() { diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java index 39a79e283..ab9e80164 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java @@ -2,9 +2,10 @@ import java.util.Scanner; -public class ConsoleInputHandler { +public class ConsoleInputHandler implements InputHandler { public static final Scanner SCANNER = new Scanner(System.in); + @Override public String getUserInput() { return SCANNER.nextLine(); } diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java index c96d79691..1d9a244a5 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -6,13 +6,15 @@ import java.util.List; import java.util.stream.IntStream; -public class ConsoleOutputHandler { +public class ConsoleOutputHandler implements OutputHandler { + @Override public void showGameStartCommand() { System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); System.out.println("지뢰찾기 게임 시작!"); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); } + @Override public void showBoard(GameBoard board) { String alphabets = generateColAlphabets(board); @@ -37,26 +39,32 @@ private static String generateColAlphabets(GameBoard board) { return joiningAlphabets; } + @Override public void printGameWinningComment() { System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); } + @Override public void printGameLosingComment() { System.out.println("지뢰를 밟았습니다. GAME OVER!"); } + @Override public void printCommentForSelectingCell() { System.out.println("선택할 좌표를 입력하세요. (예: a1)"); } + @Override public void printCommentFOrUserAction() { System.out.println("선택한 셀에 대한 행위를 선택하세요. (1: 오픈, 2: 깃발 꽂기)"); } + @Override public void printExceptionMessage(GameException e) { System.out.println(e.getMessage()); } + @Override public void printSimpleMessage(String message) { System.out.println(message); } diff --git a/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java new file mode 100644 index 000000000..cd862c64e --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java @@ -0,0 +1,5 @@ +package cleancode.minesweeper.tobe.io; + +public interface InputHandler { + String getUserInput(); +} diff --git a/src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java new file mode 100644 index 000000000..cf2c19f5a --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java @@ -0,0 +1,26 @@ +package cleancode.minesweeper.tobe.io; + +import cleancode.minesweeper.tobe.GameBoard; +import cleancode.minesweeper.tobe.GameException; + +import java.util.List; +import java.util.stream.IntStream; + +public interface OutputHandler { + + void showGameStartCommand(); + + void showBoard(GameBoard board); + + void printGameWinningComment(); + + void printGameLosingComment(); + + void printCommentForSelectingCell(); + + void printCommentFOrUserAction(); + + void printExceptionMessage(GameException e); + + void printSimpleMessage(String message); +} From 45b06d8cffe61d685276e4a79e00794f73b574f1 Mon Sep 17 00:00:00 2001 From: devny Date: Mon, 10 Mar 2025 16:13:33 +0900 Subject: [PATCH 19/39] =?UTF-8?q?2025=2003=2010=20by=20devny=20-=20DIP(?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=97=AD=EC=A0=84=20=EC=9B=90?= =?UTF-8?q?=EC=B9=99)=20-=20OutputHandler=20=EB=A5=BC=20=EB=B3=B4=EB=A9=B4?= =?UTF-8?q?...=20-=20print,=20show=20=EA=B0=99=EC=9D=80=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=EC=9D=B8=EB=8D=B0=20=EB=8B=A8=EC=96=B4=EB=A5=BC=20?= =?UTF-8?q?=EB=8B=A4=EB=A5=B4=EA=B2=8C=20=EC=82=AC=EC=9A=A9.=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EB=AA=85=EC=9D=84=20=ED=86=B5=EC=9D=BC=EC=8B=9C?= =?UTF-8?q?=EC=BC=9C=20=EC=A4=98=EC=95=BC=ED=95=A8=20-=20show=20=EA=B0=80?= =?UTF-8?q?=20=EC=A2=80=20=EB=8D=94=20=EC=B6=94=EC=83=81=EC=A0=81=EC=9D=B8?= =?UTF-8?q?=20=EB=8A=90=EB=82=8C.=20-=20OutputHandler=20=EC=97=90=EC=84=9C?= =?UTF-8?q?=EB=8A=94=20console=20=EC=A0=81=EC=9D=B8=20=EB=8A=90=EB=82=8C?= =?UTF-8?q?=EB=B3=B4=EB=8B=A4=EB=8A=94=20=EC=B6=94=EC=83=81=EC=A0=81?= =?UTF-8?q?=EC=9D=B8=20=EB=8B=A8=EC=96=B4=EB=A5=BC=20=EC=93=B0=EB=8A=94?= =?UTF-8?q?=EA=B2=8C=20=EC=A2=8B=EC=9D=8C=20->=20print=20=EB=A5=BC=20show?= =?UTF-8?q?=20=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleancode/minesweeper/tobe/Minesweeper.java | 14 ++++++-------- .../minesweeper/tobe/io/ConsoleOutputHandler.java | 12 ++++++------ .../minesweeper/tobe/io/OutputHandler.java | 15 ++++++--------- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index a42af4cff..3999aed08 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -3,8 +3,6 @@ import cleancode.minesweeper.tobe.game.GameInitializable; import cleancode.minesweeper.tobe.game.GameRunnable; import cleancode.minesweeper.tobe.gamelevel.GameLevel; -import cleancode.minesweeper.tobe.io.ConsoleInputHandler; -import cleancode.minesweeper.tobe.io.ConsoleOutputHandler; import cleancode.minesweeper.tobe.io.InputHandler; import cleancode.minesweeper.tobe.io.OutputHandler; @@ -44,11 +42,11 @@ public void run() { outputHandler.showBoard(gameBoard); if (doesUserWinTheGame()) { - outputHandler.printGameWinningComment(); + outputHandler.showGameWinningComment(); break; } if (doesUserLoseTheGame()) { - outputHandler.printGameLosingComment(); + outputHandler.showGameLosingComment(); break; } @@ -57,10 +55,10 @@ public void run() { actOnCell(cellInput, userActionInput); } catch (GameException e) { // 의도적인 Exception - outputHandler.printExceptionMessage(e); + outputHandler.showExceptionMessage(e); } catch (Exception e) { // 예상하지 못한 Exception - outputHandler.printSimpleMessage("프로그램에 문제가 생겼습니다."); + outputHandler.showSimpleMessage("프로그램에 문제가 생겼습니다."); // e.printStackTrace(); // 실무에서는 Antipattern 실무에서는 log 시스템에서 log 를 남기고 별도의 조치를 취함 } } @@ -105,12 +103,12 @@ private boolean doesUserChooseToPlantFlag(String userActionInput) { } private String getUserActionInputFromUser() { - outputHandler.printCommentFOrUserAction(); + outputHandler.showCommentFOrUserAction(); return inputHandler.getUserInput(); } private String getCellInputFromUser() { - outputHandler.printCommentForSelectingCell(); + outputHandler.showCommentForSelectingCell(); return inputHandler.getUserInput(); } diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java index 1d9a244a5..643e3e047 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -40,32 +40,32 @@ private static String generateColAlphabets(GameBoard board) { } @Override - public void printGameWinningComment() { + public void showGameWinningComment() { System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); } @Override - public void printGameLosingComment() { + public void showGameLosingComment() { System.out.println("지뢰를 밟았습니다. GAME OVER!"); } @Override - public void printCommentForSelectingCell() { + public void showCommentForSelectingCell() { System.out.println("선택할 좌표를 입력하세요. (예: a1)"); } @Override - public void printCommentFOrUserAction() { + public void showCommentFOrUserAction() { System.out.println("선택한 셀에 대한 행위를 선택하세요. (1: 오픈, 2: 깃발 꽂기)"); } @Override - public void printExceptionMessage(GameException e) { + public void showExceptionMessage(GameException e) { System.out.println(e.getMessage()); } @Override - public void printSimpleMessage(String message) { + public void showSimpleMessage(String message) { System.out.println(message); } } diff --git a/src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java index cf2c19f5a..49b800e2a 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java @@ -3,24 +3,21 @@ import cleancode.minesweeper.tobe.GameBoard; import cleancode.minesweeper.tobe.GameException; -import java.util.List; -import java.util.stream.IntStream; - public interface OutputHandler { void showGameStartCommand(); void showBoard(GameBoard board); - void printGameWinningComment(); + void showGameWinningComment(); - void printGameLosingComment(); + void showGameLosingComment(); - void printCommentForSelectingCell(); + void showCommentForSelectingCell(); - void printCommentFOrUserAction(); + void showCommentFOrUserAction(); - void printExceptionMessage(GameException e); + void showExceptionMessage(GameException e); - void printSimpleMessage(String message); + void showSimpleMessage(String message); } From 6b0305c6738129bb8eaba0a925d4ab814bc98977 Mon Sep 17 00:00:00 2001 From: devny Date: Tue, 11 Mar 2025 10:16:22 +0900 Subject: [PATCH 20/39] =?UTF-8?q?2025=2003=2011=20by=20devny=20-=20DIP(?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=97=AD=EC=A0=84=20=EC=9B=90?= =?UTF-8?q?=EC=B9=99)=EC=99=80=20DI(=EC=9D=98=EC=A1=B4=EC=84=B1=20?= =?UTF-8?q?=EC=A3=BC=EC=9E=85)=EA=B3=BC=20IoC(=EC=A0=9C=EC=96=B4=EC=9D=98?= =?UTF-8?q?=20=EC=97=AD=EC=A0=84)=EC=97=90=20=EB=8C=80=ED=95=9C=20?= =?UTF-8?q?=EC=84=A4=EB=AA=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/GameApplication.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java index eea1dec21..72c902ce2 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java @@ -19,4 +19,43 @@ public static void main(String[] args) { minesweeper.initialize(); minesweeper.run(); } + + + /** + * DIP (Dependency Inversion Principle) + * 스프링의 3대 요소 + * IOC, DI + * PSA, AOP + * + * DI = Depedency Injection + * + * IoC = Inversion of Control + * + * DIP 는 고수준 모듈과 저수준 모듈이 직접적으로 의존하는 것이 아닌 추상화에 서로 의존해야 한다. + * DI 는 의존성을 주입한다. 필요한 의존성을 내가 직접 생성하는 것이 아니라 외부에서 주입받겠다. DI 를 생각하면 떠올라야하는 숫자가 있음. 3 + * 객체(A)가 있고, 또 다른 객체(B) 하나가 있음. A 객체가 B 객체를 필요로 함. 이 둘이 의존성을 갖고 싶은데, A가 B를 생성해서 사용하는게 아니라 의존성을 주입받고 싶음. + * 생성자나 다른 메소드를 통해 주입받고자 할때 A와 B 는 주입하는 걸 할 수 있음. 그러니 제 3자가 A와 B의 의존성을 맺어줄 수 밖에 없음. + * Spring 에서는 이걸 "Spring Context"="IOC Container" 가 해줌. + * 이 것들(객체의 결정과 주입)이 Runtime 에 실행됨 + * + * DI 와 붙어다니는 IoC + * IoC 는 제어의 역전. Spring 에서만 사용되지 않음. 더 큰 개념 + * 프로그램의 흐름을 개발자가 아닌 프레임워크가 담당하도록 하는 것. + * 제어의 순방향 = 프로그램은 개발자가 만드는 것 = 내가 만든 프로그램 개발자가 제어 -> 근데 이 제어 흐름이 역전됐다. + * 내가 만든 프로그램이 미리 만들어진 공장같은 프레임워크가 있고 프레임워크 안에 요소로 내 코드가 들어가서 일부분 톱니바퀴의 하나처럼 동작하는 것. + * 프레임워크는 톱니바퀴만 빠져있는 것. 내가 톱니바퀴만 하나 만들어서 내 애플리케이션이야! 하면서 뾱하고 끼우면 됨. + * 이땐 프레임워크가 메인이 되는 것임. 내 코드는 프레임 워크의 일부가 되면서. 제어가 프레임워크 쪽으로 넘어가는 것 + * Spring Framework = 코드를 입력했을때 Spring 이 제공하는 여러가지 기능을 사용하면서 규격에 맞춰서 코딩함 -> 이런 것들이 제어의 역전 + * + * 객체의 레벨에서 보면 IoC 컨테이너라는 친구가 객체를 직접적으로 생성해주고 생명주기를 관리해 줌. + * MineSweeper 가 아까는 ConsoleInputHandler 를 class 내에서 생성하고 사용함. + * IoC 컨테이너가 하는 일은. "생성과 소멸은 내가 알아서 해줄게" "객체 자체의 생명주기도 내가 알아서 다 해줄게, 의존성 주입도 DI 로 해줄게" + * "너는 쓰기만 해!!!" + * + * 객체레벨에서도 프로그램 제어권이 IoC 컨테이너라고 하는 친구에게 주도권이 있기 때문에 객체의 생성들 즉, + * Spring 에서는 Spring 이 관리하는 객체들을 Bean 이라고 하는데 Spring 에서는 이 Bean 들을 생성하고 Bean 들끼리 의존성을 주입해주고 + * 생명주기를 관리하는 일을 IoC 컨테이너가 하게 됨. + * + * @Component, @Service -> 내가 직접 생성 안함. IoC 컨테이너가 생성해줌. + */ } \ No newline at end of file From 2c530eac321cf83b740758684f20c63c1850fd79 Mon Sep 17 00:00:00 2001 From: devny Date: Tue, 11 Mar 2025 10:44:19 +0900 Subject: [PATCH 21/39] =?UTF-8?q?2025=2003=2011=20by=20devny=20-=20Cell=20?= =?UTF-8?q?=EC=9D=98=20isFlagged,=20isOpened=20=EB=A5=BC=20=EB=B3=84?= =?UTF-8?q?=EB=8F=84=EC=9D=98=20=EA=B0=9D=EC=B2=B4=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20-=20Cell=20=EC=9D=98=20isFlagged,=20isOpened=20?= =?UTF-8?q?=EB=A5=BC=20=EB=B3=84=EB=8F=84=EC=9D=98=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC=EB=A5=BC=20=ED=95=B4=EC=84=9C=20?= =?UTF-8?q?=EA=B0=81=EA=B0=81=EC=9D=98=20=EC=9E=90=EC=8B=9D=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=EC=97=90=EC=84=9C=20=EC=A1=B0=ED=95=A9=EC=9D=98=20?= =?UTF-8?q?=ED=98=95=ED=83=9C=EB=A1=9C=20=EA=B0=80=EC=A0=B8=EB=8B=A4?= =?UTF-8?q?=EA=B0=80=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=20=ED=98=95?= =?UTF-8?q?=ED=83=9C=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=B4=EB=B3=B4?= =?UTF-8?q?=EA=B2=A0=EB=8B=A4.=20-=20=EC=B6=94=EC=83=81=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=EC=9D=B8=20Cell=20=EC=9D=84=20=EC=A7=80?= =?UTF-8?q?=EC=9A=B0=EA=B3=A0=20Cell=20=EC=9D=B4=20=EC=83=81=EC=86=8D?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=EC=97=90=EC=84=9C=20=EB=91=90=EA=B0=80?= =?UTF-8?q?=EC=A7=80=20=ED=95=84=EB=93=9C=EB=A5=BC=20CellState=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=EB=A1=9C=20=EB=B6=84=EB=A6=AC=ED=96=88=EA=B3=A0=20?= =?UTF-8?q?=EA=B0=81=EA=B0=81=EC=9D=98=20=EC=9E=90=EC=8B=9D=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=EB=93=A4=EC=9D=B4=20CellState=20=EC=99=80=20?= =?UTF-8?q?=EA=B0=81=EA=B0=81=EC=9D=98=20=EC=A1=B0=ED=95=A9=20=ED=98=95?= =?UTF-8?q?=ED=83=9C=EB=A1=9C=20=EC=9D=B4=EB=A3=A8=EC=96=B4=EC=A0=B8=20?= =?UTF-8?q?=EC=9E=88=EA=B3=A0=20Cell=20=EC=9D=84=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=EB=A1=9C=20=EB=8B=A4=EC=8B=9C=20?= =?UTF-8?q?=EB=A7=8C=EB=93=A0=20=EA=B2=83=EC=9D=80=20GameBoard=20=EC=99=80?= =?UTF-8?q?=EC=9D=98=20=EA=B4=80=EB=A0=A8=EC=A0=90=20=EB=95=8C=EB=AC=B8?= =?UTF-8?q?=EC=97=90=20=EC=8A=A4=ED=8E=99=EB=93=A4=EC=9D=84=20=EB=82=A8?= =?UTF-8?q?=EA=B2=A8=EB=91=90=EA=B3=A0=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4(Cell)=EB=A5=BC=20=EA=B4=80=EB=A0=A8=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B5=AC=ED=98=84=EC=B2=B4(=EC=9E=90=EC=8B=9Dcell)?= =?UTF-8?q?=20=EB=93=A4=EC=9D=84=20=EB=B3=80=EA=B2=BD=20=ED=95=B4=EC=A4=8C?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleancode/minesweeper/tobe/GameBoard.java | 21 +++++------ .../cleancode/minesweeper/tobe/cell/Cell.java | 36 ------------------ .../minesweeper/tobe/cell/Cell2.java | 22 +++++++++++ .../minesweeper/tobe/cell/CellState.java | 37 +++++++++++++++++++ .../minesweeper/tobe/cell/EmptyCell.java | 27 ++++++++++++-- .../minesweeper/tobe/cell/LandMineCell.java | 27 ++++++++++++-- .../minesweeper/tobe/cell/NumberCell.java | 27 ++++++++++++-- 7 files changed, 140 insertions(+), 57 deletions(-) delete mode 100644 src/main/java/cleancode/minesweeper/tobe/cell/Cell.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/cell/Cell2.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/cell/CellState.java diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index 1f97dc65d..6d8afcbf2 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -1,34 +1,31 @@ package cleancode.minesweeper.tobe; -import cleancode.minesweeper.tobe.cell.Cell; -import cleancode.minesweeper.tobe.cell.EmptyCell; -import cleancode.minesweeper.tobe.cell.LandMineCell; -import cleancode.minesweeper.tobe.cell.NumberCell; +import cleancode.minesweeper.tobe.cell.*; import cleancode.minesweeper.tobe.gamelevel.GameLevel; import java.util.Arrays; import java.util.Random; public class GameBoard { - private final Cell[][] board; + private final Cell2[][] board; private final int landMineCount; public GameBoard(GameLevel gameLevel) { int rowSize = gameLevel.getRowSize(); int colSize = gameLevel.getColSize(); - board = new Cell[rowSize][colSize]; + board = new Cell2[rowSize][colSize]; landMineCount = gameLevel.getLandMineCount(); } public void flag(int rowIndex, int colIndex) { - Cell cell = findCell(rowIndex, colIndex); + Cell2 cell = findCell(rowIndex, colIndex); cell.flag(); } public boolean isLandMineCell(int selectedRowIndex, int selectedColIndex) { - Cell cell = findCell(selectedRowIndex, selectedColIndex); + Cell2 cell = findCell(selectedRowIndex, selectedColIndex); return cell.isLandMine(); } @@ -105,15 +102,15 @@ public boolean isAllCellChecked() { // 그냥 Map 을 하면 Stream> 이 나오는데 flatMap 을 하면서 평탄화를 통해 이중배열을 배열로, 즉, Stream 으로 만들어주는 것 .flatMap(stringArr -> Arrays.stream(stringArr)) // flatMap 을 하면 Stream 이 생기는데 이 stringArray 를 하나씩 돌면서 다시 Stream 만들거다 // 여기까지가 Stream - .allMatch(Cell::isChecked); + .allMatch(Cell2::isChecked); } public String getSign(int rowIndex, int colIndex) { - Cell cell = findCell(rowIndex, colIndex); + Cell2 cell = findCell(rowIndex, colIndex); return cell.getSign(); } - private Cell findCell(int rowIndex, int colIndex) { + private Cell2 findCell(int rowIndex, int colIndex) { return board[rowIndex][colIndex]; } @@ -122,7 +119,7 @@ public int getColSize() { } public void open(int rowIndex, int colIndex) { - Cell cell = findCell(rowIndex, colIndex); + Cell2 cell = findCell(rowIndex, colIndex); cell.open(); } diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java b/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java deleted file mode 100644 index a36c2c68c..000000000 --- a/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java +++ /dev/null @@ -1,36 +0,0 @@ -package cleancode.minesweeper.tobe.cell; - -public abstract class Cell { - protected static final String FLAG_SIGN = "⚑"; - protected static final String UNCHECKED_SIGN = "□"; - - // 보드를 기반으로 먼저 만들어 보겠다. - // final 인 데이터는 최대한 final 로 만들어야 함 - protected boolean isFlagged; - protected boolean isOpened; - // Cell 이 가진 속성: 근처 지뢰 숫자, 지뢰 여부 - // Cell의 상태: 깃발 유무, 열렸다/닫혔다, 사용자가 확인함 - - public abstract boolean isLandMine(); - - public abstract boolean hasLandMineCount(); - - public abstract String getSign(); - - // flag, open, isChecked 는 다 공통일 것 같음 - public void flag() { - this.isFlagged = true; - } - - public void open() { - this.isOpened = true; - } - - public boolean isChecked() { - return isFlagged || isOpened; // 닫혀있는데 깃발이 꽂혀있거나, 열었거나 - } - - public boolean isOpened() { - return isOpened; - } -} diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/Cell2.java b/src/main/java/cleancode/minesweeper/tobe/cell/Cell2.java new file mode 100644 index 000000000..fdc538e50 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/cell/Cell2.java @@ -0,0 +1,22 @@ +package cleancode.minesweeper.tobe.cell; + +public interface Cell2 { + String FLAG_SIGN = "⚑"; + String UNCHECKED_SIGN = "□"; + + boolean isLandMine(); + + boolean hasLandMineCount(); + + String getSign(); + + // flag, open, isChecked 는 다 공통일 것 같음 + void flag(); + + void open(); + + boolean isChecked(); + + boolean isOpened(); + +} diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/CellState.java b/src/main/java/cleancode/minesweeper/tobe/cell/CellState.java new file mode 100644 index 000000000..793f5d7c2 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/cell/CellState.java @@ -0,0 +1,37 @@ +package cleancode.minesweeper.tobe.cell; + +public class CellState { + private boolean isFlagged; + private boolean isOpened; + + private CellState(boolean isFlagged, boolean isOpened) { + this.isFlagged = isFlagged; + this.isOpened = isOpened; + } + + // 정적팩토리 메소드 + public static CellState initialize() { + return new CellState(false, false); + } + + // flag, open, isChecked 는 다 공통일 것 같음 + public void flag() { + this.isFlagged = true; + } + + public void open() { + this.isOpened = true; + } + + public boolean isChecked() { + return isFlagged || isOpened; // 닫혀있는데 깃발이 꽂혀있거나, 열었거나 + } + + public boolean isOpened() { + return isOpened; + } + + public boolean isFlagged() { + return isFlagged; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java index 8b85ec01d..91fea54ce 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java @@ -1,7 +1,8 @@ package cleancode.minesweeper.tobe.cell; -public class EmptyCell extends Cell { +public class EmptyCell implements Cell2 { protected static final String EMPTY_SIGN = "■"; + private final CellState cellState = CellState.initialize(); @Override public boolean isLandMine() { @@ -15,12 +16,32 @@ public boolean hasLandMineCount() { @Override public String getSign() { - if (isOpened) { + if (cellState.isOpened()) { return EMPTY_SIGN; } - if (isFlagged) { + if (cellState.isFlagged()) { return FLAG_SIGN; } return UNCHECKED_SIGN; } + + @Override + public void flag() { + cellState.flag(); + } + + @Override + public void open() { + cellState.open(); + } + + @Override + public boolean isChecked() { + return cellState.isChecked(); + } + + @Override + public boolean isOpened() { + return cellState.isOpened(); + } } diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java index f7d48d6d4..4e7727ca1 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java @@ -1,7 +1,8 @@ package cleancode.minesweeper.tobe.cell; -public class LandMineCell extends Cell { +public class LandMineCell implements Cell2 { private static final String LAND_MINE_SIGN = "☼"; + private final CellState cellState = CellState.initialize(); @Override public boolean isLandMine() { @@ -15,12 +16,32 @@ public boolean hasLandMineCount() { @Override public String getSign() { - if (isOpened) { + if (cellState.isOpened()) { return LAND_MINE_SIGN; } - if (isFlagged) { + if (cellState.isFlagged()) { return FLAG_SIGN; } return UNCHECKED_SIGN; } + + @Override + public void flag() { + cellState.flag(); + } + + @Override + public void open() { + cellState.open(); + } + + @Override + public boolean isChecked() { + return cellState.isChecked(); + } + + @Override + public boolean isOpened() { + return cellState.isOpened(); + } } diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java index 84e9e90a2..ee888bbd9 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java @@ -1,7 +1,8 @@ package cleancode.minesweeper.tobe.cell; -public class NumberCell extends Cell { +public class NumberCell implements Cell2 { private final int nearbyLandMineCount; // 주변 지뢰 수 + private final CellState cellState = CellState.initialize(); public NumberCell(int nearbyLandMineCount) { this.nearbyLandMineCount = nearbyLandMineCount; @@ -19,12 +20,32 @@ public boolean hasLandMineCount() { @Override public String getSign() { - if (isOpened) { + if (cellState.isOpened()) { return String.valueOf(nearbyLandMineCount); } - if (isFlagged) { + if (cellState.isFlagged()) { return FLAG_SIGN; } return UNCHECKED_SIGN; } + + @Override + public void flag() { + cellState.flag(); + } + + @Override + public void open() { + cellState.open(); + } + + @Override + public boolean isChecked() { + return cellState.isChecked(); + } + + @Override + public boolean isOpened() { + return cellState.isOpened(); + } } From a712f97574c3f9e89d81d60aec3c63b7bae3a40f Mon Sep 17 00:00:00 2001 From: devny Date: Tue, 11 Mar 2025 15:29:13 +0900 Subject: [PATCH 22/39] =?UTF-8?q?2025=2003=2011=20by=20devny=20-=20Index?= =?UTF-8?q?=20=EB=A1=9C=20=EC=9D=B4=EB=A3=A8=EC=96=B4=EC=A1=8C=EB=8D=98=20?= =?UTF-8?q?=EC=A2=8C=ED=91=9C=EB=A5=BC=20CellPosition=20=EC=9D=84=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20VO=20=EB=A1=9C=20=EB=A7=8C=EB=93=A6.=20-?= =?UTF-8?q?=20=EC=9D=B4=ED=9B=84=20CellPosition=20=EA=B8=B0=EB=B0=98?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20-=20row,=20col=20?= =?UTF-8?q?=EB=8B=A4=20=EB=94=B0=EB=A1=9C=20=EB=85=B8=EB=8A=94=EB=8D=B0=20?= =?UTF-8?q?=EB=91=90=EA=B0=9C=EA=B0=80=20=EC=9E=88=EC=96=B4=EC=95=BC=20?= =?UTF-8?q?=EC=9D=98=EB=AF=B8=EA=B0=80=20=EC=9E=88=EC=96=B4=EC=84=9C=20Cel?= =?UTF-8?q?lPosition=20=EC=9C=BC=EB=A1=9C=20=EB=A7=8C=EB=93=A6.=20-=20Cell?= =?UTF-8?q?Position=20=EC=9D=B4=20VO=20=EC=97=AD=ED=95=A0=EC=9D=84=20?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=B4=20=EC=9C=A0=ED=9A=A8?= =?UTF-8?q?=EC=84=B1=EC=97=90=20=EB=8C=80=ED=95=B4=20=EC=B2=98=EB=A6=AC?= =?UTF-8?q?=ED=96=88=EB=8A=94=EB=8D=B0=20-1=20=EB=93=B1=EC=9D=98=20?= =?UTF-8?q?=EA=B0=9C=EB=85=90=EC=97=90=20=EB=8C=80=ED=95=B4=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=ED=95=98=EB=A0=A4=EA=B3=A0=20=ED=95=98=EB=8B=A4?= =?UTF-8?q?=EB=B3=B4=EB=8B=88=20RelativePosition=20=EC=9D=84=20=EB=A7=8C?= =?UTF-8?q?=EB=93=A6.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/BoardIndexConverter.java | 16 +- .../cleancode/minesweeper/tobe/GameBoard.java | 166 +++++++++++------- .../minesweeper/tobe/Minesweeper.java | 28 +-- .../tobe/cell/{Cell2.java => Cell.java} | 2 +- .../minesweeper/tobe/cell/EmptyCell.java | 2 +- .../minesweeper/tobe/cell/LandMineCell.java | 2 +- .../minesweeper/tobe/cell/NumberCell.java | 2 +- .../tobe/io/ConsoleInputHandler.java | 16 ++ .../tobe/io/ConsoleOutputHandler.java | 4 +- .../minesweeper/tobe/io/InputHandler.java | 3 + .../tobe/position/CellPosition.java | 80 +++++++++ .../tobe/position/RelativePosition.java | 50 ++++++ 12 files changed, 284 insertions(+), 87 deletions(-) rename src/main/java/cleancode/minesweeper/tobe/cell/{Cell2.java => Cell.java} (93%) create mode 100644 src/main/java/cleancode/minesweeper/tobe/position/CellPosition.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/position/RelativePosition.java diff --git a/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java b/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java index d8aecf3fd..01f966ec0 100644 --- a/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java +++ b/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java @@ -4,27 +4,27 @@ public class BoardIndexConverter { private static final char BASE_CHAR_FOR_COL = 'a'; - public int getSelectedRowIndex(String cellInput, int rowSize) { + public int getSelectedRowIndex(String cellInput) { String cellInputRow = cellInput.substring(1); - return convertRowFrom(cellInputRow, rowSize); + return convertRowFrom(cellInputRow); } - public int getSelectedColIndex(String cellInput, int collSize) { + public int getSelectedColIndex(String cellInput) { char cellInputCol = cellInput.charAt(0); - return convertColFrom(cellInputCol, collSize); + return convertColFrom(cellInputCol); } - private int convertRowFrom(String cellInputRow, int rowSize) { + private int convertRowFrom(String cellInputRow) { int rowIndex = Integer.parseInt(cellInputRow) - 1; - if (rowIndex < 0 || rowIndex >= rowSize) { + if (rowIndex < 0) { throw new GameException("잘못된 입력입니다."); } return rowIndex; } - private int convertColFrom(char cellInputCol, int collSize) { // 'a' // 알파벳이 j 까지만 대응되어 있음 + private int convertColFrom(char cellInputCol) { // 'a' // 알파벳이 j 까지만 대응되어 있음 int colIndex = cellInputCol - BASE_CHAR_FOR_COL; // a가 들어가면 0, b가 들어가면 1 - if (colIndex < 0 || colIndex >= collSize) { // a = 97 인데 cellInput 이 96 이면 알파벳이 아니기 때문 // 현재 대문자는 생각하지 않음. + if (colIndex < 0) { // a = 97 인데 cellInput 이 96 이면 알파벳이 아니기 때문 // 현재 대문자는 생각하지 않음. throw new GameException("잘못된 입력입니다."); } return colIndex; diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index 6d8afcbf2..9e85931fe 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -2,33 +2,45 @@ import cleancode.minesweeper.tobe.cell.*; import cleancode.minesweeper.tobe.gamelevel.GameLevel; +import cleancode.minesweeper.tobe.position.CellPosition; +import cleancode.minesweeper.tobe.position.RelativePosition; import java.util.Arrays; +import java.util.List; import java.util.Random; +import java.util.stream.Stream; public class GameBoard { - private final Cell2[][] board; + private final Cell[][] board; private final int landMineCount; public GameBoard(GameLevel gameLevel) { int rowSize = gameLevel.getRowSize(); int colSize = gameLevel.getColSize(); - board = new Cell2[rowSize][colSize]; + board = new Cell[rowSize][colSize]; landMineCount = gameLevel.getLandMineCount(); } - public void flag(int rowIndex, int colIndex) { - Cell2 cell = findCell(rowIndex, colIndex); + public void flagAt(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); cell.flag(); } - public boolean isLandMineCell(int selectedRowIndex, int selectedColIndex) { - Cell2 cell = findCell(selectedRowIndex, selectedColIndex); + public boolean isLandMineCellAt(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); return cell.isLandMine(); } + public boolean isInvalidCellPosition(CellPosition cellPosition) { + int rowSize = getRowSize(); + int colSize = getColSize(); + + return cellPosition.isRowIndexMoreThanOrEqual(rowSize) + || cellPosition.isColIndexMoreThanOrEqual(colSize); + } + public void initializeGame() { int rowSize = getRowSize(); int colSize = getColSize(); @@ -48,12 +60,12 @@ public void initializeGame() { for (int row = 0; row < rowSize; row++) { for (int col = 0; col < colSize; col++) { - int count = 0; - if (isLandMineCell(row, col)) { + CellPosition cellPosition = CellPosition.of(row, col); + if (isLandMineCellAt(cellPosition)) { // 이미 셀의 기본속성을 0으로 바꿔서 따로 안해도 됨 continue; } - count = countNearbyLandMines(row, col, count); + int count = countNearbyLandMines(cellPosition); if (count == 0) // 기존에는 Cell 하나여서 괜찮았지만 이제 NumberCell 이 생겼으니 count == 0 일땐 NumberCell 을 생성하지 않도록 해야함 continue; board[row][col] = new NumberCell(count);; @@ -62,35 +74,51 @@ public void initializeGame() { } } - private int countNearbyLandMines(int row, int col, int count) { - int rowSize = board.length; - int colSize = board[0].length; + private int countNearbyLandMines(CellPosition cellPosition) { + int rowSize = getRowSize(); + int colSize = getColSize(); - if (row - 1 >= 0 && col - 1 >= 0 && isLandMineCell(row - 1, col - 1)) { - count++; - } - if (row - 1 >= 0 && isLandMineCell(row - 1, col)) { - count++; - } - if (row - 1 >= 0 && col + 1 < colSize && isLandMineCell(row - 1, col + 1)) { - count++; - } - if (col - 1 >= 0 && isLandMineCell(row, col - 1)) { - count++; - } - if (col + 1 < colSize && isLandMineCell(row, col + 1)) { - count++; - } - if (row + 1 < rowSize && col - 1 >= 0 && isLandMineCell(row + 1, col - 1)) { - count++; - } - if (row + 1 < rowSize && isLandMineCell(row + 1, col)) { - count++; - } - if (row + 1 < rowSize && col + 1 < colSize && isLandMineCell(row + 1, col + 1)) { - count++; - } - return count; + long count = calculateSurroundedPositions(cellPosition, rowSize, colSize).stream() + .filter(this::isLandMineCellAt) // 이거 지뢰Cell 이야? + .count(); + + return (int) count; + +// int count = 0; +// if (row - 1 >= 0 && col - 1 >= 0 && isLandMineCellAt(row - 1, col - 1)) { +// count++; +// } +// if (row - 1 >= 0 && isLandMineCellAt(row - 1, col)) { +// count++; +// } +// if (row - 1 >= 0 && col + 1 < colSize && isLandMineCellAt(row - 1, col + 1)) { +// count++; +// } +// if (col - 1 >= 0 && isLandMineCellAt(row, col - 1)) { +// count++; +// } +// if (col + 1 < colSize && isLandMineCellAt(row, col + 1)) { +// count++; +// } +// if (row + 1 < rowSize && col - 1 >= 0 && isLandMineCellAt(row + 1, col - 1)) { +// count++; +// } +// if (row + 1 < rowSize && isLandMineCellAt(row + 1, col)) { +// count++; +// } +// if (row + 1 < rowSize && col + 1 < colSize && isLandMineCellAt(row + 1, col + 1)) { +// count++; +// } +// return count; + } + + private List calculateSurroundedPositions(CellPosition cellPosition, int rowSize, int colSize) { + return RelativePosition.SURROUNDED_POSITION.stream() + .filter(relativePosition -> cellPosition.canCalculatePositionBy(relativePosition)) // relativePosition 이 계산 가능한 Position 이야? 0 이상이야? + .map(relativePosition -> cellPosition.calculatePositionBy(relativePosition)) // 그러면 relativePosition 으로 새로운 좌표 계산해! + .filter(position -> position.isRowIndexLessThan(rowSize)) // 근데 이거 boardSize 이상이야? + .filter(position -> position.isColIndexLessThan(colSize)) + .toList(); } public int getRowSize() { @@ -102,65 +130,77 @@ public boolean isAllCellChecked() { // 그냥 Map 을 하면 Stream> 이 나오는데 flatMap 을 하면서 평탄화를 통해 이중배열을 배열로, 즉, Stream 으로 만들어주는 것 .flatMap(stringArr -> Arrays.stream(stringArr)) // flatMap 을 하면 Stream 이 생기는데 이 stringArray 를 하나씩 돌면서 다시 Stream 만들거다 // 여기까지가 Stream - .allMatch(Cell2::isChecked); + .allMatch(Cell::isChecked); } - public String getSign(int rowIndex, int colIndex) { - Cell2 cell = findCell(rowIndex, colIndex); + public String getSign(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); return cell.getSign(); } - private Cell2 findCell(int rowIndex, int colIndex) { - return board[rowIndex][colIndex]; + private Cell findCell(CellPosition cellPosition) { + return board[cellPosition.getRowIndex()][cellPosition.getColIndex()]; } public int getColSize() { return board[0].length; } - public void open(int rowIndex, int colIndex) { - Cell2 cell = findCell(rowIndex, colIndex); + public void openAt(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); cell.open(); } - public void openSurroundedCells(int row, int col) { - if (row < 0 || row >= getRowSize() || col < 0 || col >= getColSize()) { + public void openSurroundedCells(CellPosition cellPosition) { + if (cellPosition.isRowIndexMoreThanOrEqual(getRowSize()) + || cellPosition.isColIndexMoreThanOrEqual(getColSize())) { // 얘도 바깥에서 BoardSize 보다 큰지는 확인했지만 재귀로 연산을 하기때문에 두어야 함. return; } - if (isOpenedCell(row, col)) { + if (isOpenedCell(cellPosition)) { return; } - if (isLandMineCell(row, col)) { + if (isLandMineCellAt(cellPosition)) { return; } // 여기까지 안열렸으면 아직 안열린 cell 이니까 열어! - open(row, col); // 오픈 + openAt(cellPosition); // 오픈 - if (doesCellHaveLandMineCount(row, col)) { // 숫자가 있으면! + if (doesCellHaveLandMineCount(cellPosition)) { // 숫자가 있으면! // 열고 숫자를 초기화 한 것임 // BOARD[row][col] = Cell.ofNearbyLandMineCount(NEAR_BY_LAND_MINE_COUNTS[row][col]); return; } - // 재귀임 // 오픈을 시작하고 멈추는건 숫자일때, 지뢰일때임 - openSurroundedCells(row - 1, col - 1); - openSurroundedCells(row - 1, col); - openSurroundedCells(row - 1, col + 1); - openSurroundedCells(row, col - 1); - openSurroundedCells(row, col + 1); - openSurroundedCells(row + 1, col - 1); - openSurroundedCells(row + 1, col); - openSurroundedCells(row + 1, col + 1); + calculateSurroundedPositions(cellPosition, getRowSize(), getColSize()) + .forEach(this::openSurroundedCells); +// == +// RelativePosition.SURROUNDED_POSITION.stream() +// .filter(relativePosition -> cellPosition.canCalculatePositionBy(relativePosition)) // if 는 filter // cellPosition 이 canCalculate 한지 +// .map(relativePosition -> cellPosition.calculatePositionBy(relativePosition)) // relativePosition 이 주어졌을 때 calculatePositionBy 를 하면 새로운 cellPosition 들이 우루루 나옴 +// .filter(position -> position.isRowIndexLessThan(getRowSize())) // board RowSize 와 생성된 Cell 의 row position 체크 +// .filter(position -> position.isColIndexLessThan(getColSize())) // board ColSize 와 생성된 Cell 의 col psoition 체크 +// .forEach(this::openSurroundedCells); // 이것에 대해 openSurroundedCells 호출 +// == +// for (RelativePosition relativePosition : RelativePosition.SURROUNDED_POSITION) { +// if (cellPosition.canCalculatePositionBy(relativePosition)) { +// // 둘다 0 보다 컸을때 새로운 cellposition 을 만들 수 있음! +// CellPosition nextCellPosition = cellPosition.calculatePositionBy(relativePosition); +// openSurroundedCells(nextCellPosition); +// } +// } } - private boolean doesCellHaveLandMineCount(int row, int col) { - return findCell(row, col).hasLandMineCount(); + + private boolean doesCellHaveLandMineCount(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); + return cell.hasLandMineCount(); } - private boolean isOpenedCell(int row, int col) { - return findCell(row, col).isOpened(); + private boolean isOpenedCell(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); + return cell.isOpened(); } } diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index 3999aed08..2090435c3 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -5,6 +5,7 @@ import cleancode.minesweeper.tobe.gamelevel.GameLevel; import cleancode.minesweeper.tobe.io.InputHandler; import cleancode.minesweeper.tobe.io.OutputHandler; +import cleancode.minesweeper.tobe.position.CellPosition; // 모든 지뢰찾기 게임 로직을 여기에 둘 것임 public class Minesweeper implements GameInitializable, GameRunnable { @@ -50,7 +51,8 @@ public void run() { break; } - String cellInput = getCellInputFromUser(); +// String cellInput = getCellInputFromUser(); + CellPosition cellInput = getCellInputFromUser(); String userActionInput = getUserActionInputFromUser(); actOnCell(cellInput, userActionInput); @@ -64,25 +66,23 @@ public void run() { } } - private void actOnCell(String cellInput, String userActionInput) { - int selectedColIndex = boardIndexConverter.getSelectedColIndex(cellInput, gameBoard.getColSize()); - int selectedRowIndex = boardIndexConverter.getSelectedRowIndex(cellInput, gameBoard.getRowSize()); + private void actOnCell(CellPosition cellPosition, String userActionInput) { if (doesUserChooseToPlantFlag(userActionInput)) { - gameBoard.flag(selectedRowIndex, selectedColIndex); + gameBoard.flagAt(cellPosition); checkIfGameIsOver(); return; } if (doesUserChooseToOpenCell(userActionInput)) { - if (gameBoard.isLandMineCell(selectedRowIndex, selectedColIndex)) { - gameBoard.open(selectedColIndex, selectedColIndex); + if (gameBoard.isLandMineCellAt(cellPosition)) { + gameBoard.openAt(cellPosition); changeGameStatusToLose(); return; } - gameBoard.openSurroundedCells(selectedRowIndex, selectedColIndex); + gameBoard.openSurroundedCells(cellPosition); checkIfGameIsOver(); return; } @@ -107,10 +107,16 @@ private String getUserActionInputFromUser() { return inputHandler.getUserInput(); } - private String getCellInputFromUser() { + private CellPosition getCellInputFromUser() { outputHandler.showCommentForSelectingCell(); - - return inputHandler.getUserInput(); + CellPosition cellPosition = inputHandler.getCellPositionFromUser(); // 이때, index 로서의 기능은 할 수 있게 함. + // 보드 길이에 따른 Position 검증은 GameBoard 에! + // 한군데서 하면 되는데 너무 잘게 쪼개는거 아닌가요? -> 그렇게 느낄 수 있겠지만 책임을 조금 분리! + // 보드가 있는 곳에서 자연스럽게 검증을 해보자! + if (gameBoard.isInvalidCellPosition(cellPosition)) { + throw new GameException("잘못된 좌표를 선택하셨습니다."); + } + return cellPosition; } private boolean doesUserLoseTheGame() { diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/Cell2.java b/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java similarity index 93% rename from src/main/java/cleancode/minesweeper/tobe/cell/Cell2.java rename to src/main/java/cleancode/minesweeper/tobe/cell/Cell.java index fdc538e50..374954ea6 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/Cell2.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java @@ -1,6 +1,6 @@ package cleancode.minesweeper.tobe.cell; -public interface Cell2 { +public interface Cell { String FLAG_SIGN = "⚑"; String UNCHECKED_SIGN = "□"; diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java index 91fea54ce..b8687c83a 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java @@ -1,6 +1,6 @@ package cleancode.minesweeper.tobe.cell; -public class EmptyCell implements Cell2 { +public class EmptyCell implements Cell { protected static final String EMPTY_SIGN = "■"; private final CellState cellState = CellState.initialize(); diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java index 4e7727ca1..0d6a878fa 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java @@ -1,6 +1,6 @@ package cleancode.minesweeper.tobe.cell; -public class LandMineCell implements Cell2 { +public class LandMineCell implements Cell { private static final String LAND_MINE_SIGN = "☼"; private final CellState cellState = CellState.initialize(); diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java index ee888bbd9..c8f4da1c8 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java @@ -1,6 +1,6 @@ package cleancode.minesweeper.tobe.cell; -public class NumberCell implements Cell2 { +public class NumberCell implements Cell { private final int nearbyLandMineCount; // 주변 지뢰 수 private final CellState cellState = CellState.initialize(); diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java index ab9e80164..12fda2988 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java @@ -1,12 +1,28 @@ package cleancode.minesweeper.tobe.io; +import cleancode.minesweeper.tobe.BoardIndexConverter; +import cleancode.minesweeper.tobe.position.CellPosition; + import java.util.Scanner; public class ConsoleInputHandler implements InputHandler { public static final Scanner SCANNER = new Scanner(System.in); + private final BoardIndexConverter boardIndexConverter = new BoardIndexConverter(); + @Override public String getUserInput() { return SCANNER.nextLine(); } + + @Override + public CellPosition getCellPositionFromUser() { + String userInput = SCANNER.nextLine(); + int colIndex = boardIndexConverter.getSelectedColIndex(userInput); + int rowIndex = boardIndexConverter.getSelectedRowIndex(userInput); + + return CellPosition.of(rowIndex, colIndex); + } + + } diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java index 643e3e047..803723e7c 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -2,6 +2,7 @@ import cleancode.minesweeper.tobe.GameBoard; import cleancode.minesweeper.tobe.GameException; +import cleancode.minesweeper.tobe.position.CellPosition; import java.util.List; import java.util.stream.IntStream; @@ -23,7 +24,8 @@ public void showBoard(GameBoard board) { for (int row = 0; row < board.getRowSize(); row++) { System.out.printf("%2d ", row + 1); // 2자리 수 이상일때 col 정렬 맞출 수 있도록 함 for (int col = 0; col < board.getColSize(); col++) { - System.out.print(board.getSign(row, col) + " "); // 여기는 getter 를 안쓰는게 이상해 // 내가 여기에 보드를 그릴테니 cell 내용을 줘! + CellPosition cellPosition = CellPosition.of(row, col); + System.out.print(board.getSign(cellPosition) + " "); // 여기는 getter 를 안쓰는게 이상해 // 내가 여기에 보드를 그릴테니 cell 내용을 줘! } System.out.println(); } diff --git a/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java index cd862c64e..59f6e64bd 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java @@ -1,5 +1,8 @@ package cleancode.minesweeper.tobe.io; +import cleancode.minesweeper.tobe.position.CellPosition; + public interface InputHandler { String getUserInput(); + CellPosition getCellPositionFromUser(); } diff --git a/src/main/java/cleancode/minesweeper/tobe/position/CellPosition.java b/src/main/java/cleancode/minesweeper/tobe/position/CellPosition.java new file mode 100644 index 000000000..d40c8283c --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/position/CellPosition.java @@ -0,0 +1,80 @@ +package cleancode.minesweeper.tobe.position; + +import java.util.Objects; + +public class CellPosition { + + // final 을 붙여 불변성 + private final int rowIndex; + private final int colIndex; + + + private CellPosition(int rowIndex, int colIndex) { + if (rowIndex < 0 || colIndex < 0) { + // 게임 중에 발생되지 않기를 바라는 예외라서 GameException 이 아니라 개발자가 직접 확인해 주세요! 하는 마음으로 IllegalArgumentException 을 던짐 + throw new IllegalArgumentException("올바르지 않은 좌표입니다."); + } + this.rowIndex = rowIndex; + this.colIndex = colIndex; + } + + public static CellPosition of(int rowIndex, int colIndex) { + return new CellPosition(rowIndex, colIndex); + } + + /** + * equals 와 hashCode 재정의를 통해서 동등성 보장! + * rowIndex 와 colIndex 값이 같으면 같은 값! + */ + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + CellPosition that = (CellPosition) o; + return rowIndex == that.rowIndex && colIndex == that.colIndex; + } + + @Override + public int hashCode() { + return Objects.hash(rowIndex, colIndex); + } + + public boolean isRowIndexMoreThanOrEqual(int rowIndex) { + return this.rowIndex >= rowIndex; + } + + public boolean isColIndexMoreThanOrEqual(int colIndex) { + return this.colIndex >= colIndex; + } + + public int getRowIndex() { + return rowIndex; + } + + public int getColIndex() { + return colIndex; + } + + public boolean canCalculatePositionBy(RelativePosition relativePosition) { + return this.rowIndex + relativePosition.getDeltaRow() >= 0 + && this.colIndex+ relativePosition.getDeltaCol() >= 0; + } + + public CellPosition calculatePositionBy(RelativePosition relativePosition) { + if (this.canCalculatePositionBy(relativePosition)) { + return CellPosition.of( + this.rowIndex + relativePosition.getDeltaRow(), + this.colIndex + relativePosition.getDeltaCol() + ); + } + throw new IllegalArgumentException("움직일 수 있는 좌표가 아닙니다. 좌표입니다."); + // calculatePositionBy 만 단독으로 호출될때를 대비해서 if 문 적용. + } + + public boolean isRowIndexLessThan(int rowIndex) { + return this.rowIndex < rowIndex; + } + + public boolean isColIndexLessThan(int colIndex) { + return this.colIndex < rowIndex; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/position/RelativePosition.java b/src/main/java/cleancode/minesweeper/tobe/position/RelativePosition.java new file mode 100644 index 000000000..afbf36bd7 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/position/RelativePosition.java @@ -0,0 +1,50 @@ +package cleancode.minesweeper.tobe.position; + +import java.util.List; +import java.util.Objects; + +// 얘는 음수 값도 가질 수 있기 때문에 딱히 유효성 검증은 하지 않아도될듯! +public class RelativePosition { + public static final List SURROUNDED_POSITION = List.of( + RelativePosition.of(-1, -1), + RelativePosition.of(-1, 0), + RelativePosition.of(-1, 1), + RelativePosition.of(0, -1), + RelativePosition.of(0, 1), + RelativePosition.of(1, -1), + RelativePosition.of(1, 0), + RelativePosition.of(1, 1) + ); + + private final int deltaRow; + private final int deltaCol; + + private RelativePosition(int deltaRow, int deltaCol) { + this.deltaRow = deltaRow; + this.deltaCol = deltaCol; + } + + public static RelativePosition of(int deltaRow, int deltaCol) { + return new RelativePosition(deltaRow, deltaCol); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + RelativePosition that = (RelativePosition) o; + return deltaRow == that.deltaRow && deltaCol == that.deltaCol; + } + + @Override + public int hashCode() { + return Objects.hash(deltaRow, deltaCol); + } + + public int getDeltaRow() { + return deltaRow; + } + + public int getDeltaCol() { + return deltaCol; + } +} From d8f762e75d8154fa63089b45e5a2d218b69b4eff Mon Sep 17 00:00:00 2001 From: devny Date: Wed, 12 Mar 2025 08:35:41 +0900 Subject: [PATCH 23/39] =?UTF-8?q?2025=2003=2012=20by=20devny=20-=20?= =?UTF-8?q?=EC=9D=BC=EA=B8=89=20=EC=BB=AC=EB=A0=89=EC=85=98=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1(Cells)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleancode/minesweeper/tobe/GameBoard.java | 15 ++++++--- .../minesweeper/tobe/cell/Cells.java | 33 +++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/cell/Cells.java diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index 9e85931fe..e0ab8c42f 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -126,11 +126,16 @@ public int getRowSize() { } public boolean isAllCellChecked() { - return Arrays.stream(board)// BOARD 라는 이중 string 배열에 stream 을 걸면 String[] 형태의 Stream 이 나옴 Stream - // 그냥 Map 을 하면 Stream> 이 나오는데 flatMap 을 하면서 평탄화를 통해 이중배열을 배열로, 즉, Stream 으로 만들어주는 것 - .flatMap(stringArr -> Arrays.stream(stringArr)) // flatMap 을 하면 Stream 이 생기는데 이 stringArray 를 하나씩 돌면서 다시 Stream 만들거다 - // 여기까지가 Stream - .allMatch(Cell::isChecked); +// return Arrays.stream(board)// BOARD 라는 이중 string 배열에 stream 을 걸면 String[] 형태의 Stream 이 나옴 Stream +// // 그냥 Map 을 하면 Stream> 이 나오는데 flatMap 을 하면서 평탄화를 통해 이중배열을 배열로, 즉, Stream 으로 만들어주는 것 +// .flatMap(stringArr -> Arrays.stream(stringArr)) // flatMap 을 하면 Stream 이 생기는데 이 stringArray 를 하나씩 돌면서 다시 Stream 만들거다 +// // 여기까지가 Stream +// .allMatch(Cell::isChecked); + + // cell 을 가공하던 책임이 cells 안으로 들어가면서 목록을 구성하는 책임이 Cells 안으로 들어감 + Cells cells = Cells.from(board); + return cells.isAllChecked(); + } public String getSign(CellPosition cellPosition) { diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/Cells.java b/src/main/java/cleancode/minesweeper/tobe/cell/Cells.java new file mode 100644 index 000000000..bb0832a5e --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/cell/Cells.java @@ -0,0 +1,33 @@ +package cleancode.minesweeper.tobe.cell; + +import java.util.Arrays; +import java.util.List; + +// 일급 컬렉션 조건은 필드가 하나다! 컬렉션은 하나! +// 컬렉션에는 리스트, Set, Map 모두 올 수 있음 +public class Cells { + + private final List cells; + + private Cells(List cells) { + this.cells = cells; + } + + public static Cells of(List cells) { + return new Cells(cells); + } + + // Cells 가 cellsList 에 대한 가공의 책임을 가져가게 되고 + public static Cells from(Cell[][] cells) { + List cellsList = Arrays.stream(cells) + .flatMap(c -> Arrays.stream(c)) + .toList(); + + return of(cellsList); + } + + public boolean isAllChecked() { + return cells.stream() + .allMatch(Cell::isChecked); + } +} From 93a373b670254e4a728a9c609da296256ef1dfb6 Mon Sep 17 00:00:00 2001 From: devny Date: Wed, 12 Mar 2025 10:09:20 +0900 Subject: [PATCH 24/39] =?UTF-8?q?2025=2003=2012=20by=20devny=20-=20?= =?UTF-8?q?=EC=9D=BC=EA=B8=89=20=EC=BB=AC=EB=A0=89=EC=85=98=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20=ED=99=9C=EC=9A=A9(CellPositions)=20-?= =?UTF-8?q?=20=EC=A7=80=EB=A2=B0=20=EC=9C=84=EC=B9=98=EA=B0=80=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=EB=90=A0=20=EC=88=98=20=EC=9E=88=EB=8A=94=20=EC=83=81?= =?UTF-8?q?=ED=99=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleancode/minesweeper/tobe/GameBoard.java | 55 +++++++++--------- .../tobe/position/CellPositions.java | 58 +++++++++++++++++++ 2 files changed, 87 insertions(+), 26 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/position/CellPositions.java diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index e0ab8c42f..0392c599b 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -3,12 +3,10 @@ import cleancode.minesweeper.tobe.cell.*; import cleancode.minesweeper.tobe.gamelevel.GameLevel; import cleancode.minesweeper.tobe.position.CellPosition; +import cleancode.minesweeper.tobe.position.CellPositions; import cleancode.minesweeper.tobe.position.RelativePosition; -import java.util.Arrays; import java.util.List; -import java.util.Random; -import java.util.stream.Stream; public class GameBoard { private final Cell[][] board; @@ -42,38 +40,43 @@ public boolean isInvalidCellPosition(CellPosition cellPosition) { } public void initializeGame() { - int rowSize = getRowSize(); - int colSize = getColSize(); + CellPositions cellPositions = CellPositions.from(board); - for (int row = 0; row < rowSize; row++) { - for (int col = 0; col < colSize; col++) { - board[row][col] = new EmptyCell(); - } + initializeEmptyCells(cellPositions); + + List landMinePositions = cellPositions.extractRandomPositions(landMineCount); + initializeLandMineCells(landMinePositions); + + List numberPositionCandidates = cellPositions.subtract(landMinePositions); // 너가 가진 거에서 파라미터가 주어진 것을 뺀 나머지 position 을 줘 + initializeNumberCells(numberPositionCandidates); + } + + private void initializeEmptyCells(CellPositions cellPositions) { + List allPositions = cellPositions.getPositions(); + for (CellPosition position: allPositions) { + updateCellAt(position, new EmptyCell()); } + } - for (int i = 0; i < landMineCount; i++) { // 10 이 col 숫자가아니라 지뢰숫자 - int landMineCol = new Random().nextInt(colSize); - int landMineRow = new Random().nextInt(rowSize); - board[landMineRow][landMineCol] = new LandMineCell(); -// findCell(landMineRow, landMineCol).turnOnLandMine(); + private void initializeLandMineCells(List landMinePositions) { + for (CellPosition position: landMinePositions) { + updateCellAt(position, new LandMineCell()); } + } - for (int row = 0; row < rowSize; row++) { - for (int col = 0; col < colSize; col++) { - CellPosition cellPosition = CellPosition.of(row, col); - if (isLandMineCellAt(cellPosition)) { - // 이미 셀의 기본속성을 0으로 바꿔서 따로 안해도 됨 - continue; - } - int count = countNearbyLandMines(cellPosition); - if (count == 0) // 기존에는 Cell 하나여서 괜찮았지만 이제 NumberCell 이 생겼으니 count == 0 일땐 NumberCell 을 생성하지 않도록 해야함 - continue; - board[row][col] = new NumberCell(count);; -// findCell(row, col).updateNearbyLandMineCount(count); + private void initializeNumberCells(List numberPositionCandidates) { + for (CellPosition candidatePosition : numberPositionCandidates) { + int count = countNearbyLandMines(candidatePosition); + if (count != 0) { + updateCellAt(candidatePosition, new NumberCell(count)); } } } + private void updateCellAt(CellPosition position, Cell cell) { + board[position.getRowIndex()][position.getColIndex()] = cell; + } + private int countNearbyLandMines(CellPosition cellPosition) { int rowSize = getRowSize(); int colSize = getColSize(); diff --git a/src/main/java/cleancode/minesweeper/tobe/position/CellPositions.java b/src/main/java/cleancode/minesweeper/tobe/position/CellPositions.java new file mode 100644 index 000000000..892286ddc --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/position/CellPositions.java @@ -0,0 +1,58 @@ +package cleancode.minesweeper.tobe.position; + +import cleancode.minesweeper.tobe.cell.Cell; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class CellPositions { + + private final List positions; + + private CellPositions(List positions) { + this.positions = positions; + } + + public static CellPositions of(List positions) { + return new CellPositions(positions); + } + + public static CellPositions from(Cell[][] board) { + List cellPositions = new ArrayList<>(); + + for (int row = 0; row < board.length; row++) { + for (int col = 0; col < board[0].length; col++) { + CellPosition cellPosition = CellPosition.of(row, col); + cellPositions.add(cellPosition); + } + } + + return of(cellPositions); + } + + public List extractRandomPositions(int count) { // 얘는 landMineCount 인지 모름 + // shuffle 할때 그냥 컬렉션(positions) 가져다 쓰면 순서가 뒤죽박죽 될 수 있음. + // 순서가 주ㅇ요할때는 새롭게 컬렉션을 만들어라 + List cellPositions = new ArrayList<>(positions); + Collections.shuffle(cellPositions); + return cellPositions.subList(0, count); // 앞쪽에서 count 갯수만큼 자름 + } + + public List subtract(List positionsListToSubtract) { + List cellPositions = new ArrayList<>(positions); // 전체 positions 리스트로 새로 생성 + CellPositions positionsToSubtract = CellPositions.of(positionsListToSubtract); // 뺄 positions 객체로 새로 생성2 + + return cellPositions.stream() + .filter(position -> positionsToSubtract.doesNotContain(position)) + .toList(); + } + + private boolean doesNotContain(CellPosition position) { + return !positions.contains(position); + } + + public List getPositions() { + return new ArrayList<>(positions); // 외부에서 참조할 수 없도록 positions 을 새롭게 만들어서 리턴해줌 + } +} From d981303725742ab868ddb8a2243b457469c46a24 Mon Sep 17 00:00:00 2001 From: devny Date: Wed, 12 Mar 2025 15:16:59 +0900 Subject: [PATCH 25/39] =?UTF-8?q?2025=2003=2012=20by=20devny=20-=20Enum?= =?UTF-8?q?=EC=9D=98=20=ED=8A=B9=EC=84=B1=EA=B3=BC=20=ED=99=9C=EC=9A=A9=20?= =?UTF-8?q?-=20UserAction=20=EC=9D=84=20Enum=20=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=ED=95=98=EC=97=AC=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?=ED=95=A8=20-=20Cell=20=EC=9D=84=20=EB=B3=B4=EB=8B=88=20Cell=20?= =?UTF-8?q?=EC=9D=84=20=EC=96=B4=EB=96=BB=EA=B2=8C=20=EA=B7=B8=EB=A6=AC?= =?UTF-8?q?=EB=8A=94=EA=B0=80=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=B1=85?= =?UTF-8?q?=EC=9E=84=EC=9D=B4=20=EC=9E=88=EC=97=88=EC=9D=8C.=20SRP=20?= =?UTF-8?q?=EC=9C=84=EB=B0=98.=20=EC=9D=B4=20=EB=B6=80=EB=B6=84=20ConsoleO?= =?UTF-8?q?utputHandler=20=EC=9D=98=20=EC=B1=85=EC=9E=84=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=ED=95=B4=EC=95=BC=20=EB=90=A8.=20-=20=EC=9D=B4=EB=9F=AC?= =?UTF-8?q?=EB=8B=88=20Cell=20=EC=9D=B4=20=EC=96=B4=EB=96=A4=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EC=9D=B8=EC=A7=80=EA=B0=80=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=B4.=20=ED=98=84=EC=9E=AC=20Cell=20=EC=9D=98=20=ED=98=84?= =?UTF-8?q?=EC=9E=AC=20=EC=83=81=ED=83=9C=EB=A5=BC=20=EC=B0=8D=EC=96=B4?= =?UTF-8?q?=EC=A3=BC=EB=8A=94=20=EA=B2=83=20-=20CellSnapshotStatus=20Enum?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=20-=20CellSnapShot=20=EB=8F=84=20?= =?UTF-8?q?=EB=B3=B4=EB=A9=B4=20=EC=A7=80=EA=B8=88=20Value=20Object=20?= =?UTF-8?q?=EC=99=80=20=EA=B0=99=EC=9D=8C.=20SnapshotStatus=20=EA=B0=80=20?= =?UTF-8?q?=EA=B0=99=EA=B3=A0=20nearbyLandmines=20=EA=B0=80=20=EA=B0=99?= =?UTF-8?q?=EC=9C=BC=EB=A9=B4=20=EA=B0=99=EC=9D=80=20Cell=20-=20=EA=B7=B8?= =?UTF-8?q?=EB=9F=BC=20equals=20=EC=99=80=20hashCode=20=EC=9E=AC=EC=A0=95?= =?UTF-8?q?=EC=9D=98=20=ED=95=B4=EC=A4=98=EC=95=BC=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleancode/minesweeper/tobe/GameBoard.java | 10 ++-- .../minesweeper/tobe/Minesweeper.java | 23 ++++---- .../cleancode/minesweeper/tobe/cell/Cell.java | 5 +- .../minesweeper/tobe/cell/CellSnapShot.java | 58 +++++++++++++++++++ .../tobe/cell/CellSnapshotStatus.java | 16 +++++ .../minesweeper/tobe/cell/EmptyCell.java | 9 ++- .../minesweeper/tobe/cell/LandMineCell.java | 9 ++- .../minesweeper/tobe/cell/NumberCell.java | 8 +-- .../tobe/io/ConsoleInputHandler.java | 15 ++++- .../tobe/io/ConsoleOutputHandler.java | 32 +++++++++- .../minesweeper/tobe/io/InputHandler.java | 5 +- .../minesweeper/tobe/user/UserAction.java | 14 +++++ 12 files changed, 166 insertions(+), 38 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/cell/CellSnapShot.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshotStatus.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/user/UserAction.java diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index 0392c599b..e01e61ec6 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -39,6 +39,11 @@ public boolean isInvalidCellPosition(CellPosition cellPosition) { || cellPosition.isColIndexMoreThanOrEqual(colSize); } + public CellSnapShot getSnapshot(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); + return cell.getSnapshot(); + } + public void initializeGame() { CellPositions cellPositions = CellPositions.from(board); @@ -141,11 +146,6 @@ public boolean isAllCellChecked() { } - public String getSign(CellPosition cellPosition) { - Cell cell = findCell(cellPosition); - return cell.getSign(); - } - private Cell findCell(CellPosition cellPosition) { return board[cellPosition.getRowIndex()][cellPosition.getColIndex()]; } diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index 2090435c3..61ccb0e04 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -6,6 +6,7 @@ import cleancode.minesweeper.tobe.io.InputHandler; import cleancode.minesweeper.tobe.io.OutputHandler; import cleancode.minesweeper.tobe.position.CellPosition; +import cleancode.minesweeper.tobe.user.UserAction; // 모든 지뢰찾기 게임 로직을 여기에 둘 것임 public class Minesweeper implements GameInitializable, GameRunnable { @@ -53,9 +54,9 @@ public void run() { // String cellInput = getCellInputFromUser(); CellPosition cellInput = getCellInputFromUser(); - String userActionInput = getUserActionInputFromUser(); + UserAction userAction = getUserActionInputFromUser(); - actOnCell(cellInput, userActionInput); + actOnCell(cellInput, userAction); } catch (GameException e) { // 의도적인 Exception outputHandler.showExceptionMessage(e); @@ -66,16 +67,16 @@ public void run() { } } - private void actOnCell(CellPosition cellPosition, String userActionInput) { + private void actOnCell(CellPosition cellPosition, UserAction userAction) { - if (doesUserChooseToPlantFlag(userActionInput)) { + if (doesUserChooseToPlantFlag(userAction)) { gameBoard.flagAt(cellPosition); checkIfGameIsOver(); return; } - if (doesUserChooseToOpenCell(userActionInput)) { + if (doesUserChooseToOpenCell(userAction)) { if (gameBoard.isLandMineCellAt(cellPosition)) { gameBoard.openAt(cellPosition); changeGameStatusToLose(); @@ -94,17 +95,17 @@ private void changeGameStatusToLose() { gameStatus = -1; } - private boolean doesUserChooseToOpenCell(String userActionInput) { - return userActionInput.equals("1"); + private boolean doesUserChooseToOpenCell(UserAction userAction) { + return userAction == UserAction.OPEN; } - private boolean doesUserChooseToPlantFlag(String userActionInput) { - return userActionInput.equals("2"); + private boolean doesUserChooseToPlantFlag(UserAction userAction) { + return userAction == UserAction.FLAG; } - private String getUserActionInputFromUser() { + private UserAction getUserActionInputFromUser() { outputHandler.showCommentFOrUserAction(); - return inputHandler.getUserInput(); + return inputHandler.getUserActionFromUser() ; } private CellPosition getCellInputFromUser() { diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java b/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java index 374954ea6..47161a5a4 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java @@ -1,15 +1,11 @@ package cleancode.minesweeper.tobe.cell; public interface Cell { - String FLAG_SIGN = "⚑"; - String UNCHECKED_SIGN = "□"; boolean isLandMine(); boolean hasLandMineCount(); - String getSign(); - // flag, open, isChecked 는 다 공통일 것 같음 void flag(); @@ -19,4 +15,5 @@ public interface Cell { boolean isOpened(); + CellSnapShot getSnapshot(); } diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapShot.java b/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapShot.java new file mode 100644 index 000000000..abdcf31a4 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapShot.java @@ -0,0 +1,58 @@ +package cleancode.minesweeper.tobe.cell; + +import java.util.Objects; + +public class CellSnapShot { + + private final CellSnapshotStatus status; + private final int nearbyLandMineCount; + + private CellSnapShot(CellSnapshotStatus status, int nearbyLandMineCount) { + this.status = status; + this.nearbyLandMineCount = nearbyLandMineCount; + } + + public static CellSnapShot of(CellSnapshotStatus status, int nearbyLandMineCount) { + return new CellSnapShot(status, nearbyLandMineCount); + } + + public static CellSnapShot ofEmpty() { + return new CellSnapShot(CellSnapshotStatus.EMPTY, 0); + } + + public static CellSnapShot ofFlag() { + return new CellSnapShot(CellSnapshotStatus.FLAG, 0); + } + + public static CellSnapShot ofLandMine () { + return new CellSnapShot(CellSnapshotStatus.LAND_MINE, 0); + } + + public static CellSnapShot ofNumber(int nearbyLandMineCount) { + return new CellSnapShot(CellSnapshotStatus.NUMBER, nearbyLandMineCount); + } + public static CellSnapShot ofUnChecked() { + return new CellSnapShot(CellSnapshotStatus.UNCHECKED, 0); + } + + public CellSnapshotStatus getStatus() { + return status; + } + + public int getNearbyLandMineCount() { + return nearbyLandMineCount; + } + + // value object 니까! + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + CellSnapShot snapShot = (CellSnapShot) o; + return nearbyLandMineCount == snapShot.nearbyLandMineCount && status == snapShot.status; + } + + @Override + public int hashCode() { + return Objects.hash(status, nearbyLandMineCount); + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshotStatus.java b/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshotStatus.java new file mode 100644 index 000000000..54008afdd --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshotStatus.java @@ -0,0 +1,16 @@ +package cleancode.minesweeper.tobe.cell; + +public enum CellSnapshotStatus { + + EMPTY("빈 셀"), + FLAG("깃발"), + LAND_MINE("지뢰"), + NUMBER("숫자"), + UNCHECKED("확인 전"); + + private final String description; + + CellSnapshotStatus(String description) { + this.description = description; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java index b8687c83a..9421372a0 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java @@ -1,7 +1,6 @@ package cleancode.minesweeper.tobe.cell; public class EmptyCell implements Cell { - protected static final String EMPTY_SIGN = "■"; private final CellState cellState = CellState.initialize(); @Override @@ -15,14 +14,14 @@ public boolean hasLandMineCount() { } @Override - public String getSign() { + public CellSnapShot getSnapshot() { if (cellState.isOpened()) { - return EMPTY_SIGN; + return CellSnapShot.ofEmpty(); } if (cellState.isFlagged()) { - return FLAG_SIGN; + return CellSnapShot.ofFlag(); } - return UNCHECKED_SIGN; + return CellSnapShot.ofUnChecked(); } @Override diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java index 0d6a878fa..30c256629 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java @@ -1,7 +1,6 @@ package cleancode.minesweeper.tobe.cell; public class LandMineCell implements Cell { - private static final String LAND_MINE_SIGN = "☼"; private final CellState cellState = CellState.initialize(); @Override @@ -15,14 +14,14 @@ public boolean hasLandMineCount() { } @Override - public String getSign() { + public CellSnapShot getSnapshot() { if (cellState.isOpened()) { - return LAND_MINE_SIGN; + return CellSnapShot.ofLandMine(); } if (cellState.isFlagged()) { - return FLAG_SIGN; + return CellSnapShot.ofFlag(); } - return UNCHECKED_SIGN; + return CellSnapShot.ofUnChecked(); } @Override diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java index c8f4da1c8..3130d2530 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java @@ -19,14 +19,14 @@ public boolean hasLandMineCount() { } @Override - public String getSign() { + public CellSnapShot getSnapshot() { if (cellState.isOpened()) { - return String.valueOf(nearbyLandMineCount); + return CellSnapShot.ofNumber(nearbyLandMineCount); } if (cellState.isFlagged()) { - return FLAG_SIGN; + return CellSnapShot.ofFlag(); } - return UNCHECKED_SIGN; + return CellSnapShot.ofUnChecked(); } @Override diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java index 12fda2988..b4c658fa9 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java @@ -2,6 +2,7 @@ import cleancode.minesweeper.tobe.BoardIndexConverter; import cleancode.minesweeper.tobe.position.CellPosition; +import cleancode.minesweeper.tobe.user.UserAction; import java.util.Scanner; @@ -11,8 +12,18 @@ public class ConsoleInputHandler implements InputHandler { private final BoardIndexConverter boardIndexConverter = new BoardIndexConverter(); @Override - public String getUserInput() { - return SCANNER.nextLine(); + public UserAction getUserActionFromUser() { + String userInput = SCANNER.nextLine(); + + if("1".equals(userInput)) { + return UserAction.OPEN; + } + + if("2".equals(userInput)) { + return UserAction.FLAG; + } + + return UserAction.UNKNOWN; } @Override diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java index 803723e7c..986603c3d 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -2,12 +2,19 @@ import cleancode.minesweeper.tobe.GameBoard; import cleancode.minesweeper.tobe.GameException; +import cleancode.minesweeper.tobe.cell.CellSnapShot; +import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; import cleancode.minesweeper.tobe.position.CellPosition; import java.util.List; import java.util.stream.IntStream; public class ConsoleOutputHandler implements OutputHandler { + private static final String LAND_MINE_SIGN = "☼"; + protected static final String EMPTY_SIGN = "■"; + String FLAG_SIGN = "⚑"; + String UNCHECKED_SIGN = "□"; + @Override public void showGameStartCommand() { System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); @@ -25,13 +32,36 @@ public void showBoard(GameBoard board) { System.out.printf("%2d ", row + 1); // 2자리 수 이상일때 col 정렬 맞출 수 있도록 함 for (int col = 0; col < board.getColSize(); col++) { CellPosition cellPosition = CellPosition.of(row, col); - System.out.print(board.getSign(cellPosition) + " "); // 여기는 getter 를 안쓰는게 이상해 // 내가 여기에 보드를 그릴테니 cell 내용을 줘! + CellSnapShot snapShot = board.getSnapshot(cellPosition); + String cellSign = decideCellSignFrom(snapShot); + System.out.print(cellSign + " "); +// System.out.print(board.getSign(cellPosition) + " "); // 여기는 getter 를 안쓰는게 이상해 // 내가 여기에 보드를 그릴테니 cell 내용을 줘! } System.out.println(); } System.out.println(); } + private String decideCellSignFrom(CellSnapShot snapShot) { + CellSnapshotStatus status = snapShot.getStatus(); + if (status == CellSnapshotStatus.EMPTY) { + return EMPTY_SIGN; + } + if (status == CellSnapshotStatus.FLAG) { + return FLAG_SIGN; + } + if (status == CellSnapshotStatus.LAND_MINE) { + return LAND_MINE_SIGN; + } + if (status == CellSnapshotStatus.NUMBER) { + return String.valueOf(snapShot.getNearbyLandMineCount()); + } + if (status == CellSnapshotStatus.UNCHECKED) { + return UNCHECKED_SIGN; + } + throw new IllegalArgumentException("확인할 수 없는 셀입니다."); // 개발자가 확인해야 하는 Exception // 사용자가 확인해야하는 Exception = Game Exception + } + private static String generateColAlphabets(GameBoard board) { List alphabets = IntStream.range(0, board.getColSize()) // 0 부터 colSize 까지 range 만듦 .mapToObj(index -> (char)('a' + index)) // 'a' + 0 = 'a' diff --git a/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java index 59f6e64bd..05271ecee 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java @@ -1,8 +1,11 @@ package cleancode.minesweeper.tobe.io; import cleancode.minesweeper.tobe.position.CellPosition; +import cleancode.minesweeper.tobe.user.UserAction; public interface InputHandler { - String getUserInput(); + + UserAction getUserActionFromUser(); + CellPosition getCellPositionFromUser(); } diff --git a/src/main/java/cleancode/minesweeper/tobe/user/UserAction.java b/src/main/java/cleancode/minesweeper/tobe/user/UserAction.java new file mode 100644 index 000000000..1c25e50ba --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/user/UserAction.java @@ -0,0 +1,14 @@ +package cleancode.minesweeper.tobe.user; + +public enum UserAction { + + OPEN("셀 열기"), + FLAG("깃발 꽂기"), + UNKNOWN("알 수 없음"); + + private final String description; + + UserAction(String description) { + this.description = description; + } +} From a32aa691f25edaadacc848fa2b30029f24530660 Mon Sep 17 00:00:00 2001 From: devny Date: Wed, 12 Mar 2025 16:15:49 +0900 Subject: [PATCH 26/39] =?UTF-8?q?2025=2003=2012=20by=20devny=20-=20CellSna?= =?UTF-8?q?pShot.java=20->=20CellSnapshot=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleancode/minesweeper/tobe/GameBoard.java | 2 +- .../cleancode/minesweeper/tobe/cell/Cell.java | 2 +- .../{CellSnapShot.java => CellSnapshot.java} | 30 +++++++++---------- .../minesweeper/tobe/cell/EmptyCell.java | 8 ++--- .../minesweeper/tobe/cell/LandMineCell.java | 8 ++--- .../minesweeper/tobe/cell/NumberCell.java | 8 ++--- .../tobe/io/ConsoleOutputHandler.java | 6 ++-- 7 files changed, 32 insertions(+), 32 deletions(-) rename src/main/java/cleancode/minesweeper/tobe/cell/{CellSnapShot.java => CellSnapshot.java} (51%) diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index e01e61ec6..f5a5f9165 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -39,7 +39,7 @@ public boolean isInvalidCellPosition(CellPosition cellPosition) { || cellPosition.isColIndexMoreThanOrEqual(colSize); } - public CellSnapShot getSnapshot(CellPosition cellPosition) { + public CellSnapshot getSnapshot(CellPosition cellPosition) { Cell cell = findCell(cellPosition); return cell.getSnapshot(); } diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java b/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java index 47161a5a4..8f3e91aa9 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java @@ -15,5 +15,5 @@ public interface Cell { boolean isOpened(); - CellSnapShot getSnapshot(); + CellSnapshot getSnapshot(); } diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapShot.java b/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshot.java similarity index 51% rename from src/main/java/cleancode/minesweeper/tobe/cell/CellSnapShot.java rename to src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshot.java index abdcf31a4..3edf004c5 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapShot.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshot.java @@ -2,37 +2,37 @@ import java.util.Objects; -public class CellSnapShot { +public class CellSnapshot { private final CellSnapshotStatus status; private final int nearbyLandMineCount; - private CellSnapShot(CellSnapshotStatus status, int nearbyLandMineCount) { + private CellSnapshot(CellSnapshotStatus status, int nearbyLandMineCount) { this.status = status; this.nearbyLandMineCount = nearbyLandMineCount; } - public static CellSnapShot of(CellSnapshotStatus status, int nearbyLandMineCount) { - return new CellSnapShot(status, nearbyLandMineCount); + public static CellSnapshot of(CellSnapshotStatus status, int nearbyLandMineCount) { + return new CellSnapshot(status, nearbyLandMineCount); } - public static CellSnapShot ofEmpty() { - return new CellSnapShot(CellSnapshotStatus.EMPTY, 0); + public static CellSnapshot ofEmpty() { + return new CellSnapshot(CellSnapshotStatus.EMPTY, 0); } - public static CellSnapShot ofFlag() { - return new CellSnapShot(CellSnapshotStatus.FLAG, 0); + public static CellSnapshot ofFlag() { + return new CellSnapshot(CellSnapshotStatus.FLAG, 0); } - public static CellSnapShot ofLandMine () { - return new CellSnapShot(CellSnapshotStatus.LAND_MINE, 0); + public static CellSnapshot ofLandMine () { + return new CellSnapshot(CellSnapshotStatus.LAND_MINE, 0); } - public static CellSnapShot ofNumber(int nearbyLandMineCount) { - return new CellSnapShot(CellSnapshotStatus.NUMBER, nearbyLandMineCount); + public static CellSnapshot ofNumber(int nearbyLandMineCount) { + return new CellSnapshot(CellSnapshotStatus.NUMBER, nearbyLandMineCount); } - public static CellSnapShot ofUnChecked() { - return new CellSnapShot(CellSnapshotStatus.UNCHECKED, 0); + public static CellSnapshot ofUnChecked() { + return new CellSnapshot(CellSnapshotStatus.UNCHECKED, 0); } public CellSnapshotStatus getStatus() { @@ -47,7 +47,7 @@ public int getNearbyLandMineCount() { @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; - CellSnapShot snapShot = (CellSnapShot) o; + CellSnapshot snapShot = (CellSnapshot) o; return nearbyLandMineCount == snapShot.nearbyLandMineCount && status == snapShot.status; } diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java index 9421372a0..4f6f36eb0 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java @@ -14,14 +14,14 @@ public boolean hasLandMineCount() { } @Override - public CellSnapShot getSnapshot() { + public CellSnapshot getSnapshot() { if (cellState.isOpened()) { - return CellSnapShot.ofEmpty(); + return CellSnapshot.ofEmpty(); } if (cellState.isFlagged()) { - return CellSnapShot.ofFlag(); + return CellSnapshot.ofFlag(); } - return CellSnapShot.ofUnChecked(); + return CellSnapshot.ofUnChecked(); } @Override diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java index 30c256629..d081e37d3 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java @@ -14,14 +14,14 @@ public boolean hasLandMineCount() { } @Override - public CellSnapShot getSnapshot() { + public CellSnapshot getSnapshot() { if (cellState.isOpened()) { - return CellSnapShot.ofLandMine(); + return CellSnapshot.ofLandMine(); } if (cellState.isFlagged()) { - return CellSnapShot.ofFlag(); + return CellSnapshot.ofFlag(); } - return CellSnapShot.ofUnChecked(); + return CellSnapshot.ofUnChecked(); } @Override diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java b/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java index 3130d2530..897956854 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java @@ -19,14 +19,14 @@ public boolean hasLandMineCount() { } @Override - public CellSnapShot getSnapshot() { + public CellSnapshot getSnapshot() { if (cellState.isOpened()) { - return CellSnapShot.ofNumber(nearbyLandMineCount); + return CellSnapshot.ofNumber(nearbyLandMineCount); } if (cellState.isFlagged()) { - return CellSnapShot.ofFlag(); + return CellSnapshot.ofFlag(); } - return CellSnapShot.ofUnChecked(); + return CellSnapshot.ofUnChecked(); } @Override diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java index 986603c3d..020b7809f 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -2,7 +2,7 @@ import cleancode.minesweeper.tobe.GameBoard; import cleancode.minesweeper.tobe.GameException; -import cleancode.minesweeper.tobe.cell.CellSnapShot; +import cleancode.minesweeper.tobe.cell.CellSnapshot; import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; import cleancode.minesweeper.tobe.position.CellPosition; @@ -32,7 +32,7 @@ public void showBoard(GameBoard board) { System.out.printf("%2d ", row + 1); // 2자리 수 이상일때 col 정렬 맞출 수 있도록 함 for (int col = 0; col < board.getColSize(); col++) { CellPosition cellPosition = CellPosition.of(row, col); - CellSnapShot snapShot = board.getSnapshot(cellPosition); + CellSnapshot snapShot = board.getSnapshot(cellPosition); String cellSign = decideCellSignFrom(snapShot); System.out.print(cellSign + " "); // System.out.print(board.getSign(cellPosition) + " "); // 여기는 getter 를 안쓰는게 이상해 // 내가 여기에 보드를 그릴테니 cell 내용을 줘! @@ -42,7 +42,7 @@ public void showBoard(GameBoard board) { System.out.println(); } - private String decideCellSignFrom(CellSnapShot snapShot) { + private String decideCellSignFrom(CellSnapshot snapShot) { CellSnapshotStatus status = snapShot.getStatus(); if (status == CellSnapshotStatus.EMPTY) { return EMPTY_SIGN; From ac00cf07cacce53cf53a0666daec0a727d912127 Mon Sep 17 00:00:00 2001 From: devny Date: Wed, 12 Mar 2025 16:38:25 +0900 Subject: [PATCH 27/39] =?UTF-8?q?2025=2003=2012=20by=20devny=20-=20?= =?UTF-8?q?=EB=8B=A4=ED=98=95=EC=84=B1=EC=9C=BC=EB=A1=9C=20=EB=B0=98?= =?UTF-8?q?=EB=B3=B5=EB=90=98=EB=8A=94=20if=20=EB=AC=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=ED=95=98=EA=B8=B0=201.=205=EA=B0=9C=EC=9D=98=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=B2=B4=20=EB=A7=8C=EB=93=A6.=202.=20CellPo?= =?UTF-8?q?sition=20=EB=94=B0=EB=9D=BC=20snapshot=20=EC=9D=84=20board=20?= =?UTF-8?q?=EB=A1=9C=EB=B6=80=ED=84=B0=20=EA=B0=80=EC=A0=B8=EC=98=A4?= =?UTF-8?q?=EA=B3=A0,=20snapshot=20=EC=9D=84=20=ED=86=B5=ED=95=B4=EC=84=9C?= =?UTF-8?q?=20CellSignFinder=20=EC=97=90=20CellSign=20=EC=A2=80=20?= =?UTF-8?q?=EC=B0=BE=EC=95=84=EB=8B=AC=EB=9D=BC=EA=B3=A0=20=EC=B1=85?= =?UTF-8?q?=EC=9E=84=EC=9D=84=20=EB=8B=A4=20=EB=B6=80=EC=97=AC=ED=95=A8.?= =?UTF-8?q?=203.=20ConsoleOutputHandler=20=EC=97=90=EC=84=9C=EB=8A=94=20Ce?= =?UTF-8?q?llSign=20=EC=96=B4=EB=96=BB=EA=B2=8C=20=EC=83=9D=EA=B2=BC?= =?UTF-8?q?=EB=8A=94=EC=A7=80=20=EB=AA=A8=EB=A6=84.=20CellSignFinder=20?= =?UTF-8?q?=EC=97=90=EA=B2=8C=20snapshot=20=EC=A4=84=ED=85=8C=EB=8B=88=20?= =?UTF-8?q?=EB=A7=9E=EB=8A=94=EA=B1=B0=20=EC=B0=BE=EC=95=84=EC=A4=98~?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/cell/CellSnapshot.java | 6 +++- .../tobe/io/ConsoleOutputHandler.java | 29 ++----------------- .../tobe/io/sign/CellSignFinder.java | 24 +++++++++++++++ .../tobe/io/sign/CellSignProvidable.java | 13 +++++++++ .../tobe/io/sign/EmptyCellSignProvider.java | 18 ++++++++++++ .../tobe/io/sign/FlagCellSignProvider.java | 18 ++++++++++++ .../io/sign/LandMineCellSignProvider.java | 18 ++++++++++++ .../tobe/io/sign/NumberCellSignProvider.java | 17 +++++++++++ .../io/sign/UncheckedCellSignProvider.java | 19 ++++++++++++ 9 files changed, 135 insertions(+), 27 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignFinder.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvidable.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/io/sign/EmptyCellSignProvider.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/io/sign/FlagCellSignProvider.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/io/sign/LandMineCellSignProvider.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/io/sign/NumberCellSignProvider.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/io/sign/UncheckedCellSignProvider.java diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshot.java b/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshot.java index 3edf004c5..6d4a5f83b 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshot.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshot.java @@ -35,6 +35,10 @@ public static CellSnapshot ofUnChecked() { return new CellSnapshot(CellSnapshotStatus.UNCHECKED, 0); } + public boolean isSameStatus(CellSnapshotStatus cellSnapshotStatus) { + return this.status == cellSnapshotStatus; // 들어온 status 랑 내가 갖고있는 status 가 같은지 확인 + } + public CellSnapshotStatus getStatus() { return status; } @@ -42,8 +46,8 @@ public CellSnapshotStatus getStatus() { public int getNearbyLandMineCount() { return nearbyLandMineCount; } - // value object 니까! + @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java index 020b7809f..4169cbf62 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -3,17 +3,14 @@ import cleancode.minesweeper.tobe.GameBoard; import cleancode.minesweeper.tobe.GameException; import cleancode.minesweeper.tobe.cell.CellSnapshot; -import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; +import cleancode.minesweeper.tobe.io.sign.*; import cleancode.minesweeper.tobe.position.CellPosition; import java.util.List; import java.util.stream.IntStream; public class ConsoleOutputHandler implements OutputHandler { - private static final String LAND_MINE_SIGN = "☼"; - protected static final String EMPTY_SIGN = "■"; - String FLAG_SIGN = "⚑"; - String UNCHECKED_SIGN = "□"; + private final CellSignFinder cellSignFinder = new CellSignFinder(); @Override public void showGameStartCommand() { @@ -33,7 +30,7 @@ public void showBoard(GameBoard board) { for (int col = 0; col < board.getColSize(); col++) { CellPosition cellPosition = CellPosition.of(row, col); CellSnapshot snapShot = board.getSnapshot(cellPosition); - String cellSign = decideCellSignFrom(snapShot); + String cellSign = cellSignFinder.findCellSignFrom(snapShot); System.out.print(cellSign + " "); // System.out.print(board.getSign(cellPosition) + " "); // 여기는 getter 를 안쓰는게 이상해 // 내가 여기에 보드를 그릴테니 cell 내용을 줘! } @@ -42,26 +39,6 @@ public void showBoard(GameBoard board) { System.out.println(); } - private String decideCellSignFrom(CellSnapshot snapShot) { - CellSnapshotStatus status = snapShot.getStatus(); - if (status == CellSnapshotStatus.EMPTY) { - return EMPTY_SIGN; - } - if (status == CellSnapshotStatus.FLAG) { - return FLAG_SIGN; - } - if (status == CellSnapshotStatus.LAND_MINE) { - return LAND_MINE_SIGN; - } - if (status == CellSnapshotStatus.NUMBER) { - return String.valueOf(snapShot.getNearbyLandMineCount()); - } - if (status == CellSnapshotStatus.UNCHECKED) { - return UNCHECKED_SIGN; - } - throw new IllegalArgumentException("확인할 수 없는 셀입니다."); // 개발자가 확인해야 하는 Exception // 사용자가 확인해야하는 Exception = Game Exception - } - private static String generateColAlphabets(GameBoard board) { List alphabets = IntStream.range(0, board.getColSize()) // 0 부터 colSize 까지 range 만듦 .mapToObj(index -> (char)('a' + index)) // 'a' + 0 = 'a' diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignFinder.java b/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignFinder.java new file mode 100644 index 000000000..9e6de2ac7 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignFinder.java @@ -0,0 +1,24 @@ +package cleancode.minesweeper.tobe.io.sign; + +import cleancode.minesweeper.tobe.cell.CellSnapshot; + +import java.util.List; + +public class CellSignFinder { + + public static final List CELL_SIGN_PROVIDERS = List.of( + new EmptyCellSignProvider(), + new FlagCellSignProvider(), + new LandMineCellSignProvider(), + new NumberCellSignProvider(), + new UncheckedCellSignProvider() + ); + + public String findCellSignFrom(CellSnapshot snapshot) { + return CELL_SIGN_PROVIDERS.stream() + .filter(provider -> provider.supports(snapshot)) // provider 의 supports 가 현재 snapShot 과 같은가? true 인거 하나 나옴 + .findFirst() + .map(provider -> provider.provide(snapshot)) // 문양 제공해줘 = 문양 뽑아냄 + .orElseThrow(() -> new IllegalArgumentException("확인할 수 없는 셀입니다.")); // 혹시 없는 경우는 예외 던짐 + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvidable.java b/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvidable.java new file mode 100644 index 000000000..6ce273dcb --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvidable.java @@ -0,0 +1,13 @@ +package cleancode.minesweeper.tobe.io.sign; + +import cleancode.minesweeper.tobe.cell.CellSnapshot; + +// cellsnapshot 을 넘겨줬을때 sign 을 return 하는 함수 +public interface CellSignProvidable { + + boolean supports(CellSnapshot cellSnapshot); + + String provide(CellSnapshot cellSnapshot); // cellSnapshot 을 받는 이유는 numberCell 값을 받기 위해 + + +} diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/EmptyCellSignProvider.java b/src/main/java/cleancode/minesweeper/tobe/io/sign/EmptyCellSignProvider.java new file mode 100644 index 000000000..e4056e312 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/EmptyCellSignProvider.java @@ -0,0 +1,18 @@ +package cleancode.minesweeper.tobe.io.sign; + +import cleancode.minesweeper.tobe.cell.CellSnapshot; +import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; + +public class EmptyCellSignProvider implements CellSignProvidable { + protected static final String EMPTY_SIGN = "■"; + + @Override + public boolean supports(CellSnapshot cellSnapshot) { + return cellSnapshot.isSameStatus(CellSnapshotStatus.EMPTY); + } + + @Override + public String provide(CellSnapshot cellSnapshot) { + return EMPTY_SIGN; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/FlagCellSignProvider.java b/src/main/java/cleancode/minesweeper/tobe/io/sign/FlagCellSignProvider.java new file mode 100644 index 000000000..2953df35f --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/FlagCellSignProvider.java @@ -0,0 +1,18 @@ +package cleancode.minesweeper.tobe.io.sign; + +import cleancode.minesweeper.tobe.cell.CellSnapshot; +import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; + +public class FlagCellSignProvider implements CellSignProvidable { + String FLAG_SIGN = "⚑"; + + @Override + public boolean supports(CellSnapshot cellSnapshot) { + return cellSnapshot.isSameStatus(CellSnapshotStatus.FLAG); + } + + @Override + public String provide(CellSnapshot cellSnapshot) { + return FLAG_SIGN; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/LandMineCellSignProvider.java b/src/main/java/cleancode/minesweeper/tobe/io/sign/LandMineCellSignProvider.java new file mode 100644 index 000000000..3529e4e28 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/LandMineCellSignProvider.java @@ -0,0 +1,18 @@ +package cleancode.minesweeper.tobe.io.sign; + +import cleancode.minesweeper.tobe.cell.CellSnapshot; +import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; + +public class LandMineCellSignProvider implements CellSignProvidable { + private static final String LAND_MINE_SIGN = "☼"; + + @Override + public boolean supports(CellSnapshot cellSnapshot) { + return cellSnapshot.isSameStatus(CellSnapshotStatus.LAND_MINE); + } + + @Override + public String provide(CellSnapshot cellSnapshot) { + return LAND_MINE_SIGN; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/NumberCellSignProvider.java b/src/main/java/cleancode/minesweeper/tobe/io/sign/NumberCellSignProvider.java new file mode 100644 index 000000000..085fb2dca --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/NumberCellSignProvider.java @@ -0,0 +1,17 @@ +package cleancode.minesweeper.tobe.io.sign; + +import cleancode.minesweeper.tobe.cell.CellSnapshot; +import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; + +public class NumberCellSignProvider implements CellSignProvidable { + + @Override + public boolean supports(CellSnapshot cellSnapshot) { + return cellSnapshot.isSameStatus(CellSnapshotStatus.NUMBER); + } + + @Override + public String provide(CellSnapshot cellSnapshot) { + return String.valueOf(cellSnapshot.getNearbyLandMineCount()); + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/UncheckedCellSignProvider.java b/src/main/java/cleancode/minesweeper/tobe/io/sign/UncheckedCellSignProvider.java new file mode 100644 index 000000000..510524b8f --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/UncheckedCellSignProvider.java @@ -0,0 +1,19 @@ +package cleancode.minesweeper.tobe.io.sign; + +import cleancode.minesweeper.tobe.cell.CellSnapshot; +import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; + +public class UncheckedCellSignProvider implements CellSignProvidable { + String UNCHECKED_SIGN = "□"; + + + @Override + public boolean supports(CellSnapshot cellSnapshot) { + return cellSnapshot.isSameStatus(CellSnapshotStatus.UNCHECKED); + } + + @Override + public String provide(CellSnapshot cellSnapshot) { + return UNCHECKED_SIGN; + } +} From 100d3ed7c1170385a9105f02c719bbf166154145 Mon Sep 17 00:00:00 2001 From: devny Date: Wed, 12 Mar 2025 16:53:43 +0900 Subject: [PATCH 28/39] =?UTF-8?q?2025=2003=2012=20by=20devny=20-=20Enum=20?= =?UTF-8?q?=EC=9D=84=20=ED=86=B5=ED=95=B4=20=EC=B6=94=EA=B0=80=20=EC=B6=94?= =?UTF-8?q?=EC=83=81=ED=99=94=20=EC=A7=84=ED=96=89=20-=20CellSignProvider?= =?UTF-8?q?=20Enum=20=ED=81=B4=EB=9E=98=EC=8A=A4=EC=97=90=EC=84=9C=20CellS?= =?UTF-8?q?ignProvidable=20=EC=9D=84=20=EC=83=81=EC=86=8D=EB=B0=9B?= =?UTF-8?q?=EC=95=84=EC=84=9C=20=EC=95=88=EC=97=90=20=EC=98=A4=EB=B2=84?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=93=9C=20=ED=95=A8=EC=88=98=EB=A5=BC=20Enu?= =?UTF-8?q?m=20=EA=B0=92=20=EC=95=88=EC=97=90=EC=84=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=ED=95=A8.=20-=20Enum=20=EC=97=90=EA=B2=8C=20snapshot?= =?UTF-8?q?=20=EC=97=90=20=EB=A7=9E=EB=8A=94=20=EB=AC=B8=EC=96=91=20?= =?UTF-8?q?=EA=B0=80=EC=A0=B8=EC=99=80=EC=A4=98!=20=EB=9D=BC=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20-=20cellSignFinder=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tobe/io/ConsoleOutputHandler.java | 3 +- .../tobe/io/sign/CellSignProvider.java | 64 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvider.java diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java index 4169cbf62..530af3a07 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -30,7 +30,8 @@ public void showBoard(GameBoard board) { for (int col = 0; col < board.getColSize(); col++) { CellPosition cellPosition = CellPosition.of(row, col); CellSnapshot snapShot = board.getSnapshot(cellPosition); - String cellSign = cellSignFinder.findCellSignFrom(snapShot); +// String cellSign = cellSignFinder.findCellSignFrom(snapShot); + String cellSign = CellSignProvider.findCellSignFrom(snapShot); System.out.print(cellSign + " "); // System.out.print(board.getSign(cellPosition) + " "); // 여기는 getter 를 안쓰는게 이상해 // 내가 여기에 보드를 그릴테니 cell 내용을 줘! } diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvider.java b/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvider.java new file mode 100644 index 000000000..f1b55029c --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvider.java @@ -0,0 +1,64 @@ +package cleancode.minesweeper.tobe.io.sign; + +import cleancode.minesweeper.tobe.cell.CellSnapshot; +import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; + +import java.util.Arrays; + +public enum CellSignProvider implements CellSignProvidable { + + EMPTY(CellSnapshotStatus.EMPTY) { + @Override + public String provide(CellSnapshot cellSnapshot) { + return EMPTY_SIGN; + } + }, + FLAG(CellSnapshotStatus.FLAG) { + @Override + public String provide(CellSnapshot cellSnapshot) { + return FLAG_SIGN; + } + }, + LANDMINE(CellSnapshotStatus.LAND_MINE) { + @Override + public String provide(CellSnapshot cellSnapshot) { + return LAND_MINE_SIGN; + } + }, + NUMBER(CellSnapshotStatus.NUMBER) { + @Override + public String provide(CellSnapshot cellSnapshot) { + return String.valueOf(cellSnapshot.getNearbyLandMineCount()); + } + }, + UNCHECKED(CellSnapshotStatus.UNCHECKED){ + public String provide(CellSnapshot cellSnapshot) { + return UNCHECKED_SIGN; + } + }; + + private final CellSnapshotStatus status; + + private static final String EMPTY_SIGN = "■"; + private static final String FLAG_SIGN = "⚑"; + private static final String LAND_MINE_SIGN = "☼"; + private static final String UNCHECKED_SIGN = "□"; + + CellSignProvider(CellSnapshotStatus status) { + this.status = status; + } + + + @Override + public boolean supports(CellSnapshot cellSnapshot) { + return cellSnapshot.isSameStatus(status); // 각 Enum 이 각자 상태에 맞는 cellstatus 를 갖고 있기 때문에 비교 가능 + } + + public static String findCellSignFrom(CellSnapshot cellSnapshot) { + return Arrays.stream(values()) + .filter(provider -> provider.supports(cellSnapshot)) + .findFirst() + .map(provider -> provider.provide(cellSnapshot)) + .orElseThrow(() -> new IllegalArgumentException("확인할 수 없는 셀입니다.")); + } +} From c4ca94d895b33449a3a4df0df7f04544ee4a9cf1 Mon Sep 17 00:00:00 2001 From: devny Date: Wed, 12 Mar 2025 22:20:39 +0900 Subject: [PATCH 29/39] =?UTF-8?q?2025=2003=2012=20by=20devny=20-=20mine=20?= =?UTF-8?q?package=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20studycafe=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=B3=B5=EC=82=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../studycafe/mine/StudyCafeApplication.java | 10 +++ .../studycafe/mine/StudyCafePassMachine.java | 81 +++++++++++++++++++ .../mine/exception/AppException.java | 9 +++ .../studycafe/mine/io/InputHandler.java | 40 +++++++++ .../studycafe/mine/io/OutputHandler.java | 68 ++++++++++++++++ .../mine/io/StudyCafeFileHandler.java | 56 +++++++++++++ .../mine/model/StudyCafeLockerPass.java | 44 ++++++++++ .../studycafe/mine/model/StudyCafePass.java | 50 ++++++++++++ .../mine/model/StudyCafePassType.java | 15 ++++ 9 files changed, 373 insertions(+) create mode 100644 src/main/java/cleancode/studycafe/mine/StudyCafeApplication.java create mode 100644 src/main/java/cleancode/studycafe/mine/StudyCafePassMachine.java create mode 100644 src/main/java/cleancode/studycafe/mine/exception/AppException.java create mode 100644 src/main/java/cleancode/studycafe/mine/io/InputHandler.java create mode 100644 src/main/java/cleancode/studycafe/mine/io/OutputHandler.java create mode 100644 src/main/java/cleancode/studycafe/mine/io/StudyCafeFileHandler.java create mode 100644 src/main/java/cleancode/studycafe/mine/model/StudyCafeLockerPass.java create mode 100644 src/main/java/cleancode/studycafe/mine/model/StudyCafePass.java create mode 100644 src/main/java/cleancode/studycafe/mine/model/StudyCafePassType.java diff --git a/src/main/java/cleancode/studycafe/mine/StudyCafeApplication.java b/src/main/java/cleancode/studycafe/mine/StudyCafeApplication.java new file mode 100644 index 000000000..6ac7fb1a2 --- /dev/null +++ b/src/main/java/cleancode/studycafe/mine/StudyCafeApplication.java @@ -0,0 +1,10 @@ +package cleancode.studycafe.mine; + +public class StudyCafeApplication { + + public static void main(String[] args) { + StudyCafePassMachine studyCafePassMachine = new StudyCafePassMachine(); + studyCafePassMachine.run(); + } + +} diff --git a/src/main/java/cleancode/studycafe/mine/StudyCafePassMachine.java b/src/main/java/cleancode/studycafe/mine/StudyCafePassMachine.java new file mode 100644 index 000000000..36710ccb7 --- /dev/null +++ b/src/main/java/cleancode/studycafe/mine/StudyCafePassMachine.java @@ -0,0 +1,81 @@ +package cleancode.studycafe.mine; + +import cleancode.studycafe.mine.exception.AppException; +import cleancode.studycafe.mine.io.InputHandler; +import cleancode.studycafe.mine.io.OutputHandler; +import cleancode.studycafe.mine.io.StudyCafeFileHandler; +import cleancode.studycafe.mine.model.StudyCafeLockerPass; +import cleancode.studycafe.mine.model.StudyCafePass; +import cleancode.studycafe.mine.model.StudyCafePassType; + +import java.util.List; + +public class StudyCafePassMachine { + + private final InputHandler inputHandler = new InputHandler(); + private final OutputHandler outputHandler = new OutputHandler(); + + public void run() { + try { + outputHandler.showWelcomeMessage(); + outputHandler.showAnnouncement(); + + outputHandler.askPassTypeSelection(); + StudyCafePassType studyCafePassType = inputHandler.getPassTypeSelectingUserAction(); + + if (studyCafePassType == StudyCafePassType.HOURLY) { + StudyCafeFileHandler studyCafeFileHandler = new StudyCafeFileHandler(); + List studyCafePasses = studyCafeFileHandler.readStudyCafePasses(); + List hourlyPasses = studyCafePasses.stream() + .filter(studyCafePass -> studyCafePass.getPassType() == StudyCafePassType.HOURLY) + .toList(); + outputHandler.showPassListForSelection(hourlyPasses); + StudyCafePass selectedPass = inputHandler.getSelectPass(hourlyPasses); + outputHandler.showPassOrderSummary(selectedPass, null); + } else if (studyCafePassType == StudyCafePassType.WEEKLY) { + StudyCafeFileHandler studyCafeFileHandler = new StudyCafeFileHandler(); + List studyCafePasses = studyCafeFileHandler.readStudyCafePasses(); + List weeklyPasses = studyCafePasses.stream() + .filter(studyCafePass -> studyCafePass.getPassType() == StudyCafePassType.WEEKLY) + .toList(); + outputHandler.showPassListForSelection(weeklyPasses); + StudyCafePass selectedPass = inputHandler.getSelectPass(weeklyPasses); + outputHandler.showPassOrderSummary(selectedPass, null); + } else if (studyCafePassType == StudyCafePassType.FIXED) { + StudyCafeFileHandler studyCafeFileHandler = new StudyCafeFileHandler(); + List studyCafePasses = studyCafeFileHandler.readStudyCafePasses(); + List fixedPasses = studyCafePasses.stream() + .filter(studyCafePass -> studyCafePass.getPassType() == StudyCafePassType.FIXED) + .toList(); + outputHandler.showPassListForSelection(fixedPasses); + StudyCafePass selectedPass = inputHandler.getSelectPass(fixedPasses); + + List lockerPasses = studyCafeFileHandler.readLockerPasses(); + StudyCafeLockerPass lockerPass = lockerPasses.stream() + .filter(option -> + option.getPassType() == selectedPass.getPassType() + && option.getDuration() == selectedPass.getDuration() + ) + .findFirst() + .orElse(null); + + boolean lockerSelection = false; + if (lockerPass != null) { + outputHandler.askLockerPass(lockerPass); + lockerSelection = inputHandler.getLockerSelection(); + } + + if (lockerSelection) { + outputHandler.showPassOrderSummary(selectedPass, lockerPass); + } else { + outputHandler.showPassOrderSummary(selectedPass, null); + } + } + } catch (AppException e) { + outputHandler.showSimpleMessage(e.getMessage()); + } catch (Exception e) { + outputHandler.showSimpleMessage("알 수 없는 오류가 발생했습니다."); + } + } + +} diff --git a/src/main/java/cleancode/studycafe/mine/exception/AppException.java b/src/main/java/cleancode/studycafe/mine/exception/AppException.java new file mode 100644 index 000000000..a70bfca3d --- /dev/null +++ b/src/main/java/cleancode/studycafe/mine/exception/AppException.java @@ -0,0 +1,9 @@ +package cleancode.studycafe.mine.exception; + +public class AppException extends RuntimeException { + + public AppException(String message) { + super(message); + } + +} diff --git a/src/main/java/cleancode/studycafe/mine/io/InputHandler.java b/src/main/java/cleancode/studycafe/mine/io/InputHandler.java new file mode 100644 index 000000000..9345990d6 --- /dev/null +++ b/src/main/java/cleancode/studycafe/mine/io/InputHandler.java @@ -0,0 +1,40 @@ +package cleancode.studycafe.mine.io; + +import cleancode.studycafe.mine.exception.AppException; +import cleancode.studycafe.mine.model.StudyCafePass; +import cleancode.studycafe.mine.model.StudyCafePassType; + +import java.util.List; +import java.util.Scanner; + +public class InputHandler { + + private static final Scanner SCANNER = new Scanner(System.in); + + public StudyCafePassType getPassTypeSelectingUserAction() { + String userInput = SCANNER.nextLine(); + + if ("1".equals(userInput)) { + return StudyCafePassType.HOURLY; + } + if ("2".equals(userInput)) { + return StudyCafePassType.WEEKLY; + } + if ("3".equals(userInput)) { + return StudyCafePassType.FIXED; + } + throw new AppException("잘못된 입력입니다."); + } + + public StudyCafePass getSelectPass(List passes) { + String userInput = SCANNER.nextLine(); + int selectedIndex = Integer.parseInt(userInput) - 1; + return passes.get(selectedIndex); + } + + public boolean getLockerSelection() { + String userInput = SCANNER.nextLine(); + return "1".equals(userInput); + } + +} diff --git a/src/main/java/cleancode/studycafe/mine/io/OutputHandler.java b/src/main/java/cleancode/studycafe/mine/io/OutputHandler.java new file mode 100644 index 000000000..e36dcdaa8 --- /dev/null +++ b/src/main/java/cleancode/studycafe/mine/io/OutputHandler.java @@ -0,0 +1,68 @@ +package cleancode.studycafe.mine.io; + +import cleancode.studycafe.mine.model.StudyCafeLockerPass; +import cleancode.studycafe.mine.model.StudyCafePass; + +import java.util.List; + +public class OutputHandler { + + public void showWelcomeMessage() { + System.out.println("*** 프리미엄 스터디카페 ***"); + } + + public void showAnnouncement() { + System.out.println("* 사물함은 고정석 선택 시 이용 가능합니다. (추가 결제)"); + System.out.println("* !오픈 이벤트! 2주권 이상 결제 시 10% 할인, 12주권 결제 시 15% 할인! (결제 시 적용)"); + System.out.println(); + } + + public void askPassTypeSelection() { + System.out.println("사용하실 이용권을 선택해 주세요."); + System.out.println("1. 시간 이용권(자유석) | 2. 주단위 이용권(자유석) | 3. 1인 고정석"); + } + + public void showPassListForSelection(List passes) { + System.out.println(); + System.out.println("이용권 목록"); + for (int index = 0; index < passes.size(); index++) { + StudyCafePass pass = passes.get(index); + System.out.println(String.format("%s. ", index + 1) + pass.display()); + } + } + + public void askLockerPass(StudyCafeLockerPass lockerPass) { + System.out.println(); + String askMessage = String.format( + "사물함을 이용하시겠습니까? (%s)", + lockerPass.display() + ); + + System.out.println(askMessage); + System.out.println("1. 예 | 2. 아니오"); + } + + public void showPassOrderSummary(StudyCafePass selectedPass, StudyCafeLockerPass lockerPass) { + System.out.println(); + System.out.println("이용 내역"); + System.out.println("이용권: " + selectedPass.display()); + if (lockerPass != null) { + System.out.println("사물함: " + lockerPass.display()); + } + + double discountRate = selectedPass.getDiscountRate(); + int discountPrice = (int) (selectedPass.getPrice() * discountRate); + if (discountPrice > 0) { + System.out.println("이벤트 할인 금액: " + discountPrice + "원"); + } + + int totalPrice = selectedPass.getPrice() - discountPrice + (lockerPass != null ? lockerPass.getPrice() : 0); + System.out.println("총 결제 금액: " + totalPrice + "원"); + System.out.println(); + } + + public void showSimpleMessage(String message) { + System.out.println(message); + } + +} diff --git a/src/main/java/cleancode/studycafe/mine/io/StudyCafeFileHandler.java b/src/main/java/cleancode/studycafe/mine/io/StudyCafeFileHandler.java new file mode 100644 index 000000000..ea8524142 --- /dev/null +++ b/src/main/java/cleancode/studycafe/mine/io/StudyCafeFileHandler.java @@ -0,0 +1,56 @@ +package cleancode.studycafe.mine.io; + +import cleancode.studycafe.mine.model.StudyCafeLockerPass; +import cleancode.studycafe.mine.model.StudyCafePass; +import cleancode.studycafe.mine.model.StudyCafePassType; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +public class StudyCafeFileHandler { + + public List readStudyCafePasses() { + try { + List lines = Files.readAllLines(Paths.get("src/main/resources/cleancode/studycafe/pass-list.csv")); + List studyCafePasses = new ArrayList<>(); + for (String line : lines) { + String[] values = line.split(","); + StudyCafePassType studyCafePassType = StudyCafePassType.valueOf(values[0]); + int duration = Integer.parseInt(values[1]); + int price = Integer.parseInt(values[2]); + double discountRate = Double.parseDouble(values[3]); + + StudyCafePass studyCafePass = StudyCafePass.of(studyCafePassType, duration, price, discountRate); + studyCafePasses.add(studyCafePass); + } + + return studyCafePasses; + } catch (IOException e) { + throw new RuntimeException("파일을 읽는데 실패했습니다.", e); + } + } + + public List readLockerPasses() { + try { + List lines = Files.readAllLines(Paths.get("src/main/resources/cleancode/studycafe/locker.csv")); + List lockerPasses = new ArrayList<>(); + for (String line : lines) { + String[] values = line.split(","); + StudyCafePassType studyCafePassType = StudyCafePassType.valueOf(values[0]); + int duration = Integer.parseInt(values[1]); + int price = Integer.parseInt(values[2]); + + StudyCafeLockerPass lockerPass = StudyCafeLockerPass.of(studyCafePassType, duration, price); + lockerPasses.add(lockerPass); + } + + return lockerPasses; + } catch (IOException e) { + throw new RuntimeException("파일을 읽는데 실패했습니다.", e); + } + } + +} diff --git a/src/main/java/cleancode/studycafe/mine/model/StudyCafeLockerPass.java b/src/main/java/cleancode/studycafe/mine/model/StudyCafeLockerPass.java new file mode 100644 index 000000000..8663deb48 --- /dev/null +++ b/src/main/java/cleancode/studycafe/mine/model/StudyCafeLockerPass.java @@ -0,0 +1,44 @@ +package cleancode.studycafe.mine.model; + +public class StudyCafeLockerPass { + + private final StudyCafePassType passType; + private final int duration; + private final int price; + + private StudyCafeLockerPass(StudyCafePassType passType, int duration, int price) { + this.passType = passType; + this.duration = duration; + this.price = price; + } + + public static StudyCafeLockerPass of(StudyCafePassType passType, int duration, int price) { + return new StudyCafeLockerPass(passType, duration, price); + } + + public StudyCafePassType getPassType() { + return passType; + } + + public int getDuration() { + return duration; + } + + public int getPrice() { + return price; + } + + public String display() { + if (passType == StudyCafePassType.HOURLY) { + return String.format("%s시간권 - %d원", duration, price); + } + if (passType == StudyCafePassType.WEEKLY) { + return String.format("%s주권 - %d원", duration, price); + } + if (passType == StudyCafePassType.FIXED) { + return String.format("%s주권 - %d원", duration, price); + } + return ""; + } + +} diff --git a/src/main/java/cleancode/studycafe/mine/model/StudyCafePass.java b/src/main/java/cleancode/studycafe/mine/model/StudyCafePass.java new file mode 100644 index 000000000..baa785544 --- /dev/null +++ b/src/main/java/cleancode/studycafe/mine/model/StudyCafePass.java @@ -0,0 +1,50 @@ +package cleancode.studycafe.mine.model; + +public class StudyCafePass { + + private final StudyCafePassType passType; + private final int duration; + private final int price; + private final double discountRate; + + private StudyCafePass(StudyCafePassType passType, int duration, int price, double discountRate) { + this.passType = passType; + this.duration = duration; + this.price = price; + this.discountRate = discountRate; + } + + public static StudyCafePass of(StudyCafePassType passType, int duration, int price, double discountRate) { + return new StudyCafePass(passType, duration, price, discountRate); + } + + public StudyCafePassType getPassType() { + return passType; + } + + public int getDuration() { + return duration; + } + + public int getPrice() { + return price; + } + + public double getDiscountRate() { + return discountRate; + } + + public String display() { + if (passType == StudyCafePassType.HOURLY) { + return String.format("%s시간권 - %d원", duration, price); + } + if (passType == StudyCafePassType.WEEKLY) { + return String.format("%s주권 - %d원", duration, price); + } + if (passType == StudyCafePassType.FIXED) { + return String.format("%s주권 - %d원", duration, price); + } + return ""; + } + +} diff --git a/src/main/java/cleancode/studycafe/mine/model/StudyCafePassType.java b/src/main/java/cleancode/studycafe/mine/model/StudyCafePassType.java new file mode 100644 index 000000000..0bb48006e --- /dev/null +++ b/src/main/java/cleancode/studycafe/mine/model/StudyCafePassType.java @@ -0,0 +1,15 @@ +package cleancode.studycafe.mine.model; + +public enum StudyCafePassType { + + HOURLY("시간 단위 이용권"), + WEEKLY("주 단위 이용권"), + FIXED("1인 고정석"); + + private final String description; + + StudyCafePassType(String description) { + this.description = description; + } + +} From a2cb8ebe13ce3d03980510f8636c24689ebc8bed Mon Sep 17 00:00:00 2001 From: devny Date: Wed, 12 Mar 2025 23:53:15 +0900 Subject: [PATCH 30/39] =?UTF-8?q?2025=2003=2012=20by=20devny=20-=20DAY7=5F?= =?UTF-8?q?=EC=B6=94=EC=83=81=ED=99=94=20=EB=A0=88=EB=B2=A8=EB=A7=9E?= =?UTF-8?q?=EC=B6=94=EA=B8=B0=20-=20StudyCafePassMachine=20-=20StudyCafeLo?= =?UTF-8?q?ckerPass=20=20+=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20getter?= =?UTF-8?q?,=20setter=20=ED=95=A8=EC=88=98=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../studycafe/asis/StudyCafePassMachine.java | 128 ++++++++++++------ .../asis/io/StudyCafeFileHandler.java | 2 +- .../asis/model/StudyCafeLockerPass.java | 18 ++- 3 files changed, 95 insertions(+), 53 deletions(-) diff --git a/src/main/java/cleancode/studycafe/asis/StudyCafePassMachine.java b/src/main/java/cleancode/studycafe/asis/StudyCafePassMachine.java index 1e910a5f2..d274fbb89 100644 --- a/src/main/java/cleancode/studycafe/asis/StudyCafePassMachine.java +++ b/src/main/java/cleancode/studycafe/asis/StudyCafePassMachine.java @@ -11,6 +11,7 @@ import java.util.List; public class StudyCafePassMachine { + private final StudyCafeFileHandler studyCafeFileHandler = new StudyCafeFileHandler(); private final InputHandler inputHandler = new InputHandler(); private final OutputHandler outputHandler = new OutputHandler(); @@ -23,53 +24,27 @@ public void run() { outputHandler.askPassTypeSelection(); StudyCafePassType studyCafePassType = inputHandler.getPassTypeSelectingUserAction(); - if (studyCafePassType == StudyCafePassType.HOURLY) { - StudyCafeFileHandler studyCafeFileHandler = new StudyCafeFileHandler(); - List studyCafePasses = studyCafeFileHandler.readStudyCafePasses(); - List hourlyPasses = studyCafePasses.stream() - .filter(studyCafePass -> studyCafePass.getPassType() == StudyCafePassType.HOURLY) - .toList(); - outputHandler.showPassListForSelection(hourlyPasses); - StudyCafePass selectedPass = inputHandler.getSelectPass(hourlyPasses); + + + if (isPassTypeHourly(studyCafePassType)) { + StudyCafePass selectedPass = getPassTypeInputFromUser(StudyCafePassType.HOURLY); outputHandler.showPassOrderSummary(selectedPass, null); - } else if (studyCafePassType == StudyCafePassType.WEEKLY) { - StudyCafeFileHandler studyCafeFileHandler = new StudyCafeFileHandler(); - List studyCafePasses = studyCafeFileHandler.readStudyCafePasses(); - List weeklyPasses = studyCafePasses.stream() - .filter(studyCafePass -> studyCafePass.getPassType() == StudyCafePassType.WEEKLY) - .toList(); - outputHandler.showPassListForSelection(weeklyPasses); - StudyCafePass selectedPass = inputHandler.getSelectPass(weeklyPasses); + return; + } + + if (isPassTypeWeekly(studyCafePassType)) { + StudyCafePass selectedPass = getPassTypeInputFromUser(StudyCafePassType.WEEKLY); outputHandler.showPassOrderSummary(selectedPass, null); - } else if (studyCafePassType == StudyCafePassType.FIXED) { - StudyCafeFileHandler studyCafeFileHandler = new StudyCafeFileHandler(); - List studyCafePasses = studyCafeFileHandler.readStudyCafePasses(); - List fixedPasses = studyCafePasses.stream() - .filter(studyCafePass -> studyCafePass.getPassType() == StudyCafePassType.FIXED) - .toList(); - outputHandler.showPassListForSelection(fixedPasses); - StudyCafePass selectedPass = inputHandler.getSelectPass(fixedPasses); - - List lockerPasses = studyCafeFileHandler.readLockerPasses(); - StudyCafeLockerPass lockerPass = lockerPasses.stream() - .filter(option -> - option.getPassType() == selectedPass.getPassType() - && option.getDuration() == selectedPass.getDuration() - ) - .findFirst() - .orElse(null); - - boolean lockerSelection = false; - if (lockerPass != null) { - outputHandler.askLockerPass(lockerPass); - lockerSelection = inputHandler.getLockerSelection(); - } - - if (lockerSelection) { - outputHandler.showPassOrderSummary(selectedPass, lockerPass); - } else { - outputHandler.showPassOrderSummary(selectedPass, null); - } + return; + } + + if (isPassTypeFixed(studyCafePassType)) { + StudyCafePass selectedPass = getPassTypeInputFromUser(StudyCafePassType.FIXED); + StudyCafeLockerPass lockerPass = getSelectedLockerPass(selectedPass); // 고정석 가져오기 + + boolean lockerSelection = doesUserChooseToUseLocker(lockerPass); + + showAllInfo(selectedPass, lockerPass, lockerSelection); } } catch (AppException e) { outputHandler.showSimpleMessage(e.getMessage()); @@ -78,4 +53,67 @@ public void run() { } } + private boolean doesUserChooseToUseLocker(StudyCafeLockerPass lockerPass) { + boolean lockerSelection = false; + if (lockerPass != null) { + lockerSelection = getUserUseTheLocker(lockerPass); + } + return lockerSelection; + } + + private void showAllInfo(StudyCafePass selectedPass, StudyCafeLockerPass lockerPass, boolean lockerSelection) { + if (lockerSelection) { + outputHandler.showPassOrderSummary(selectedPass, lockerPass); + } else { + outputHandler.showPassOrderSummary(selectedPass, null); + } + } + + private boolean getUserUseTheLocker(StudyCafeLockerPass lockerPass) { + boolean lockerSelection; + outputHandler.askLockerPass(lockerPass); + lockerSelection = inputHandler.getLockerSelection(); + return lockerSelection; + } + + private StudyCafeLockerPass getSelectedLockerPass(StudyCafePass selectedPass) { + List lockerPasses = studyCafeFileHandler. readLockerPasses(); // FIXED,4,10000 + StudyCafeLockerPass lockerPass = lockerPasses.stream() + .filter(option -> option.isSelectedLockerPassType(selectedPass)) + .findFirst() + .orElse(null); + return lockerPass; + } + + private static boolean isPassTypeFixed(StudyCafePassType studyCafePassType) { + return studyCafePassType == StudyCafePassType.FIXED; + } + + private static boolean isPassTypeWeekly(StudyCafePassType studyCafePassType) { + return studyCafePassType == StudyCafePassType.WEEKLY; + } + + private static boolean isPassTypeHourly(StudyCafePassType studyCafePassType) { + return studyCafePassType == StudyCafePassType.HOURLY; + } + + private void showPassListForSelection(List hourlyPasses) { + outputHandler.showPassListForSelection(hourlyPasses); + } + + private StudyCafePass getPassTypeInputFromUser(StudyCafePassType passType) { + List passTypeList = getPassTypeList(passType); + showPassListForSelection(passTypeList); + StudyCafePass inputPassType = inputHandler.getSelectPass(passTypeList); + return inputPassType; + } + + private static List getPassTypeList(StudyCafePassType passType) { + StudyCafeFileHandler studyCafeFileHandler = new StudyCafeFileHandler(); + List studyCafePasses = studyCafeFileHandler.readStudyCafePasses(); + return studyCafePasses.stream() + .filter(studyCafePass -> studyCafePass.getPassType() == passType) + .toList(); + } + } diff --git a/src/main/java/cleancode/studycafe/asis/io/StudyCafeFileHandler.java b/src/main/java/cleancode/studycafe/asis/io/StudyCafeFileHandler.java index 1dc0328f6..76ed84e98 100644 --- a/src/main/java/cleancode/studycafe/asis/io/StudyCafeFileHandler.java +++ b/src/main/java/cleancode/studycafe/asis/io/StudyCafeFileHandler.java @@ -11,7 +11,7 @@ import cleancode.studycafe.asis.model.StudyCafePassType; public class StudyCafeFileHandler { - + // 이용권 목록을 파일로 읽는다. public List readStudyCafePasses() { try { List lines = Files.readAllLines(Paths.get("src/main/resources/cleancode/studycafe/pass-list.csv")); diff --git a/src/main/java/cleancode/studycafe/asis/model/StudyCafeLockerPass.java b/src/main/java/cleancode/studycafe/asis/model/StudyCafeLockerPass.java index d6cf932ae..507672eec 100644 --- a/src/main/java/cleancode/studycafe/asis/model/StudyCafeLockerPass.java +++ b/src/main/java/cleancode/studycafe/asis/model/StudyCafeLockerPass.java @@ -16,13 +16,13 @@ public static StudyCafeLockerPass of(StudyCafePassType passType, int duration, i return new StudyCafeLockerPass(passType, duration, price); } - public StudyCafePassType getPassType() { - return passType; - } - - public int getDuration() { - return duration; - } +// public StudyCafePassType getPassType() { +// return passType; +// } +// +// public int getDuration() { +// return duration; +// } public int getPrice() { return price; @@ -41,4 +41,8 @@ public String display() { return ""; } + public boolean isSelectedLockerPassType(StudyCafePass selectedPass) { + return this.passType == selectedPass.getPassType() && + this.duration == selectedPass.getDuration(); + } } From 20c2dfdf744f4d64cb05707aa43bf800fd7b7bc1 Mon Sep 17 00:00:00 2001 From: devny Date: Sun, 16 Mar 2025 00:12:15 +0900 Subject: [PATCH 31/39] =?UTF-8?q?2025=2003=2016=20by=20devny=20-=20Enum=20?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=B6=94=EC=83=81=ED=99=94=20=EC=A7=84?= =?UTF-8?q?=ED=96=89=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/io/sign/CellSignProvider.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvider.java b/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvider.java index f1b55029c..eeb0a5779 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvider.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvider.java @@ -4,6 +4,7 @@ import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; import java.util.Arrays; +import java.util.Optional; public enum CellSignProvider implements CellSignProvidable { @@ -55,10 +56,14 @@ public boolean supports(CellSnapshot cellSnapshot) { } public static String findCellSignFrom(CellSnapshot cellSnapshot) { + CellSignProvider cellSignProvider = findBy(cellSnapshot); // snapshot 에 맞는 provider 를 하나 추출한다음 + return cellSignProvider.provide(cellSnapshot); // provide 해봤다. + } + + private static CellSignProvider findBy(CellSnapshot cellSnapshot) { return Arrays.stream(values()) .filter(provider -> provider.supports(cellSnapshot)) .findFirst() - .map(provider -> provider.provide(cellSnapshot)) .orElseThrow(() -> new IllegalArgumentException("확인할 수 없는 셀입니다.")); } } From 3c7e44f1ef29a42bd6f7187f2b17f7481f82f6db Mon Sep 17 00:00:00 2001 From: devny Date: Sun, 16 Mar 2025 00:41:26 +0900 Subject: [PATCH 32/39] =?UTF-8?q?2025=2003=2016=20by=20devny=20-=20?= =?UTF-8?q?=EC=83=88=EB=A1=9C=EC=9A=B4=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EB=8F=84=EC=B6=9C=20-=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=A0=95=EB=B3=B4=EB=9D=BC=EB=8A=94=20=EA=B2=83?= =?UTF-8?q?=EC=9D=84=20=ED=95=98=EB=82=98=EB=A1=9C=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=EB=A5=BC=20=ED=95=B4=EB=B3=B4=EC=9E=90!=20=EB=82=98=EC=A4=91?= =?UTF-8?q?=EC=97=90=20=EC=84=A4=EC=A0=95=EC=A0=95=EB=B3=B4=EB=93=A4?= =?UTF-8?q?=EC=9D=B4=20=EB=8D=94=20=EC=B6=94=EA=B0=80=EB=90=98=EB=A9=B4=20?= =?UTF-8?q?=EA=B3=84=EC=86=8D=20Minesweeper=20=EC=83=9D=EC=84=B1=EC=9E=90?= =?UTF-8?q?=EB=A5=BC=20=EB=B0=94=EA=BF=94=EC=95=BC=20=ED=95=98=EB=8B=88?= =?UTF-8?q?=EA=B9=8C!=20GameApplication=20=EC=97=90=EB=8F=84=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=ED=95=B4=EC=A4=98=EC=95=BC=ED=95=98=EA=B3=A0!=20=20?= =?UTF-8?q?=20+=20GameConfig=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/GameApplication.java | 12 +++++--- .../minesweeper/tobe/Minesweeper.java | 7 +++++ .../minesweeper/tobe/config/GameConfig.java | 30 +++++++++++++++++++ .../minesweeper/tobe/io/InputHandler.java | 11 ------- 4 files changed, 45 insertions(+), 15 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/config/GameConfig.java delete mode 100644 src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java diff --git a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java index 72c902ce2..d24355748 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java @@ -1,5 +1,6 @@ package cleancode.minesweeper.tobe; +import cleancode.minesweeper.tobe.config.GameConfig; import cleancode.minesweeper.tobe.gamelevel.Advanced; import cleancode.minesweeper.tobe.gamelevel.Beginner; import cleancode.minesweeper.tobe.gamelevel.GameLevel; @@ -11,11 +12,14 @@ public class GameApplication { public static void main(String[] args) { - GameLevel gameLevel = new Beginner(); - InputHandler inputHandler = new ConsoleInputHandler(); - OutputHandler outputHandler = new ConsoleOutputHandler(); - Minesweeper minesweeper = new Minesweeper(gameLevel, inputHandler, outputHandler); + GameConfig gameConfig = new GameConfig( + new Beginner(), + new ConsoleInputHandler(), + new ConsoleOutputHandler() + ); + + Minesweeper minesweeper = new Minesweeper(gameConfig); minesweeper.initialize(); minesweeper.run(); } diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index 61ccb0e04..ab6948e38 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -1,5 +1,6 @@ package cleancode.minesweeper.tobe; +import cleancode.minesweeper.tobe.config.GameConfig; import cleancode.minesweeper.tobe.game.GameInitializable; import cleancode.minesweeper.tobe.game.GameRunnable; import cleancode.minesweeper.tobe.gamelevel.GameLevel; @@ -30,6 +31,12 @@ public Minesweeper(GameLevel gameLevel, InputHandler inputHandler, OutputHandler this.outputHandler = outputHandler; } + public Minesweeper(GameConfig gameConfig) { + gameBoard = new GameBoard(gameConfig.getGameLevel()); + this.inputHandler = gameConfig.getInputHandler(); + this.outputHandler = gameConfig.getOutputHandler(); + } + @Override public void initialize() { gameBoard.initializeGame(); diff --git a/src/main/java/cleancode/minesweeper/tobe/config/GameConfig.java b/src/main/java/cleancode/minesweeper/tobe/config/GameConfig.java new file mode 100644 index 000000000..5238acbbc --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/config/GameConfig.java @@ -0,0 +1,30 @@ +package cleancode.minesweeper.tobe.config; + +import cleancode.minesweeper.tobe.gamelevel.GameLevel; +import cleancode.minesweeper.tobe.io.InputHandler; +import cleancode.minesweeper.tobe.io.OutputHandler; + +public class GameConfig { + + private final GameLevel gameLevel; + private final InputHandler inputHandler; + private final OutputHandler outputHandler; + + public GameConfig(GameLevel gameLevel, InputHandler inputHandler, OutputHandler outputHandler) { + this.gameLevel = gameLevel; + this.inputHandler = inputHandler; + this.outputHandler = outputHandler; + } + + public GameLevel getGameLevel() { + return gameLevel; + } + + public InputHandler getInputHandler() { + return inputHandler; + } + + public OutputHandler getOutputHandler() { + return outputHandler; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java deleted file mode 100644 index 05271ecee..000000000 --- a/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java +++ /dev/null @@ -1,11 +0,0 @@ -package cleancode.minesweeper.tobe.io; - -import cleancode.minesweeper.tobe.position.CellPosition; -import cleancode.minesweeper.tobe.user.UserAction; - -public interface InputHandler { - - UserAction getUserActionFromUser(); - - CellPosition getCellPositionFromUser(); -} From efc5ee4c3267e8829da7b876b1eb6eba7558b037 Mon Sep 17 00:00:00 2001 From: devny Date: Sun, 16 Mar 2025 22:07:49 +0900 Subject: [PATCH 33/39] =?UTF-8?q?2025=2003=2016=20by=20devny=20-=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EA=B4=80=EB=A0=A8=20-=20gameStatus=20?= =?UTF-8?q?=EB=A5=BC=20Enum=20=EC=9C=BC=EB=A1=9C=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=97=86=EC=96=B4=EB=8F=84=20=EC=9D=98=EB=8F=84=EB=A5=BC=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/GameStatus.java | 16 +++++++++++++++ .../minesweeper/tobe/Minesweeper.java | 20 +++++++------------ 2 files changed, 23 insertions(+), 13 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/GameStatus.java diff --git a/src/main/java/cleancode/minesweeper/tobe/GameStatus.java b/src/main/java/cleancode/minesweeper/tobe/GameStatus.java new file mode 100644 index 000000000..f07e75d8b --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/GameStatus.java @@ -0,0 +1,16 @@ +package cleancode.minesweeper.tobe; + +public enum GameStatus { + + IN_PROGRESS("진행중"), + WIN("승리"), + LOSE("패배"), + ; + + private final String description; + + GameStatus(String description) { + this.description = description; + } +} + diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index ab6948e38..f275bd3ca 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -3,7 +3,6 @@ import cleancode.minesweeper.tobe.config.GameConfig; import cleancode.minesweeper.tobe.game.GameInitializable; import cleancode.minesweeper.tobe.game.GameRunnable; -import cleancode.minesweeper.tobe.gamelevel.GameLevel; import cleancode.minesweeper.tobe.io.InputHandler; import cleancode.minesweeper.tobe.io.OutputHandler; import cleancode.minesweeper.tobe.position.CellPosition; @@ -22,24 +21,19 @@ public class Minesweeper implements GameInitializable, GameRunnable { // 입출력에 대한건 여기서! private final InputHandler inputHandler; private final OutputHandler outputHandler; - - private int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 - - public Minesweeper(GameLevel gameLevel, InputHandler inputHandler, OutputHandler outputHandler) { - gameBoard = new GameBoard(gameLevel); - this.inputHandler = inputHandler; - this.outputHandler = outputHandler; - } + private GameStatus gameStatus; public Minesweeper(GameConfig gameConfig) { gameBoard = new GameBoard(gameConfig.getGameLevel()); this.inputHandler = gameConfig.getInputHandler(); this.outputHandler = gameConfig.getOutputHandler(); + gameStatus = GameStatus.IN_PROGRESS; } @Override public void initialize() { gameBoard.initializeGame(); + gameStatus = GameStatus.IN_PROGRESS; } @Override @@ -99,7 +93,7 @@ private void actOnCell(CellPosition cellPosition, UserAction userAction) { } private void changeGameStatusToLose() { - gameStatus = -1; + gameStatus = GameStatus.LOSE; } private boolean doesUserChooseToOpenCell(UserAction userAction) { @@ -128,11 +122,11 @@ private CellPosition getCellInputFromUser() { } private boolean doesUserLoseTheGame() { - return gameStatus == -1; + return gameStatus == GameStatus.LOSE; } private boolean doesUserWinTheGame() { - return gameStatus == 1; + return gameStatus == GameStatus.WIN; } private void checkIfGameIsOver() { @@ -142,7 +136,7 @@ private void checkIfGameIsOver() { } private void changeGameStatusToWin() { - gameStatus = 1; + gameStatus = GameStatus.WIN; } // private boolean isAllCellOpened() { From 41a18bf0bf9c0d7a78710b1cbe07143415d61d93 Mon Sep 17 00:00:00 2001 From: devny Date: Sun, 16 Mar 2025 22:14:51 +0900 Subject: [PATCH 34/39] =?UTF-8?q?2025=2003=2016=20by=20devny=20-=20?= =?UTF-8?q?=EB=AC=B4=ED=95=9C=20=EB=A3=A8=ED=94=84=20=EC=9C=84=ED=97=98?= =?UTF-8?q?=EC=84=B1=EC=9D=B4=20=EC=9E=88=EC=9D=8C.=20=EC=88=98=EC=A0=95.?= =?UTF-8?q?=20-=20while=20=EB=AC=B8=EC=9D=98=20=EC=A1=B0=EA=B1=B4=EC=9D=84?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20-=20=EA=B2=8C=EC=9E=84=EC=9D=B4=20?= =?UTF-8?q?=EB=81=9D=EB=82=9C=20=ED=9B=84=20result=20=EB=A5=BC=20while=20?= =?UTF-8?q?=EB=AC=B8=20=EB=B0=96=EC=9C=BC=EB=A1=9C=20=EB=82=B4=EB=B3=B4?= =?UTF-8?q?=EB=82=BC=20=EC=88=98=20=EC=9E=88=EC=9D=8C.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/Minesweeper.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index f275bd3ca..ed13b6de8 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -40,19 +40,10 @@ public void initialize() { public void run() { outputHandler.showGameStartCommand(); - while (true) { + while (GameStatus.IN_PROGRESS == gameStatus) { try { outputHandler.showBoard(gameBoard); - if (doesUserWinTheGame()) { - outputHandler.showGameWinningComment(); - break; - } - if (doesUserLoseTheGame()) { - outputHandler.showGameLosingComment(); - break; - } - // String cellInput = getCellInputFromUser(); CellPosition cellInput = getCellInputFromUser(); UserAction userAction = getUserActionInputFromUser(); @@ -66,6 +57,15 @@ public void run() { // e.printStackTrace(); // 실무에서는 Antipattern 실무에서는 log 시스템에서 log 를 남기고 별도의 조치를 취함 } } + + outputHandler.showBoard(gameBoard); // 마지막 결과값 보여줌 + + if (doesUserWinTheGame()) { + outputHandler.showGameWinningComment(); + } + if (doesUserLoseTheGame()) { + outputHandler.showGameLosingComment(); + } } private void actOnCell(CellPosition cellPosition, UserAction userAction) { From a1e760c710a2282669b3f1c4cc4d0183bfb67127 Mon Sep 17 00:00:00 2001 From: devny Date: Sun, 16 Mar 2025 23:24:33 +0900 Subject: [PATCH 35/39] =?UTF-8?q?2025=2003=2016=20by=20devny=20-=20gameSta?= =?UTF-8?q?tus=20=EB=A5=BC=20GameBoard=20=EB=A1=9C=20=EC=9D=B4=EA=B4=80=20?= =?UTF-8?q?-=20GameBoard=20=EC=97=90=20=EA=B3=84=EC=86=8D=20=EB=AC=BC?= =?UTF-8?q?=EC=96=B4=EB=B3=B4=EA=B3=A0=20Minesweeper=20=EA=B0=80=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=ED=95=98=EC=A7=80=EB=A7=90=EA=B3=A0=20GameBo?= =?UTF-8?q?ard=20=EC=97=90=EC=84=9C=20=EB=81=9D=EB=82=BC=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20=ED=95=A8=20-=20Game=20=EC=A7=84?= =?UTF-8?q?=ED=96=89=EC=97=90=20=EB=8C=80=ED=95=9C=20=EA=B2=83=EB=93=A4?= =?UTF-8?q?=EC=9D=80=20GameBoard=20=EA=B0=80=20=EC=8B=A4=EC=A7=88=EC=A0=81?= =?UTF-8?q?=EC=9D=B8=20=EA=B4=80=EB=A6=AC=EB=A5=BC=20=ED=95=98=EA=B3=A0=20?= =?UTF-8?q?Minesweeper=20=EB=8A=94=20=EA=B2=8C=EC=9E=84=EC=9D=84=20?= =?UTF-8?q?=EC=8B=A4=ED=96=89=ED=95=98=EA=B3=A0,=20=EC=97=AC=EB=9F=AC?= =?UTF-8?q?=EA=B0=80=EC=A7=80=20=EC=B6=9C=EB=A0=A5=EC=9D=B4=EB=82=98=20?= =?UTF-8?q?=EC=9D=B4=EB=9F=B0=20=EA=B2=83=EB=93=A4=EC=9D=84=20=EB=8B=B4?= =?UTF-8?q?=EB=8B=B9=ED=95=B4=EC=A4=8C(=EC=99=B8=EB=B6=80=EC=99=80?= =?UTF-8?q?=EC=9D=98=20=EC=86=8C=ED=86=B5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleancode/minesweeper/tobe/GameBoard.java | 51 ++++++++++++++++++- .../minesweeper/tobe/Minesweeper.java | 42 ++------------- 2 files changed, 53 insertions(+), 40 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index f5a5f9165..147a6de5f 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -11,6 +11,7 @@ public class GameBoard { private final Cell[][] board; private final int landMineCount; + private GameStatus gameStatus; public GameBoard(GameLevel gameLevel) { int rowSize = gameLevel.getRowSize(); @@ -19,11 +20,24 @@ public GameBoard(GameLevel gameLevel) { board = new Cell[rowSize][colSize]; landMineCount = gameLevel.getLandMineCount(); + initializeGameStatus(); } public void flagAt(CellPosition cellPosition) { Cell cell = findCell(cellPosition); cell.flag(); + + checkIfGameIsOver(); + + } + private void checkIfGameIsOver() { + if (isAllCellChecked()) { // 게임 이긴 것 + changeGameStatusToWin(); + } + } + + private void changeGameStatusToWin() { + gameStatus = GameStatus.WIN; } public boolean isLandMineCellAt(CellPosition cellPosition) { @@ -45,6 +59,7 @@ public CellSnapshot getSnapshot(CellPosition cellPosition) { } public void initializeGame() { + initializeGameStatus(); CellPositions cellPositions = CellPositions.from(board); initializeEmptyCells(cellPositions); @@ -56,6 +71,10 @@ public void initializeGame() { initializeNumberCells(numberPositionCandidates); } + private void initializeGameStatus() { + gameStatus = GameStatus.IN_PROGRESS; + } + private void initializeEmptyCells(CellPositions cellPositions) { List allPositions = cellPositions.getPositions(); for (CellPosition position: allPositions) { @@ -154,7 +173,7 @@ public int getColSize() { return board[0].length; } - public void openAt(CellPosition cellPosition) { + public void openOneCell(CellPosition cellPosition) { Cell cell = findCell(cellPosition); cell.open(); } @@ -174,7 +193,7 @@ public void openSurroundedCells(CellPosition cellPosition) { } // 여기까지 안열렸으면 아직 안열린 cell 이니까 열어! - openAt(cellPosition); // 오픈 + openOneCell(cellPosition); // 오픈 if (doesCellHaveLandMineCount(cellPosition)) { // 숫자가 있으면! // 열고 숫자를 초기화 한 것임 @@ -211,4 +230,32 @@ private boolean isOpenedCell(CellPosition cellPosition) { Cell cell = findCell(cellPosition); return cell.isOpened(); } + + public boolean isInProgress() { + return gameStatus == GameStatus.IN_PROGRESS; + } + + public void openAt(CellPosition cellPosition) { + if (isLandMineCellAt(cellPosition)) { + openOneCell(cellPosition); + changeGameStatusToLose(); + return; + } + + openSurroundedCells(cellPosition); + checkIfGameIsOver(); + return; + } + + private void changeGameStatusToLose() { + gameStatus = GameStatus.LOSE; + } + + public boolean isWinStatus() { + return gameStatus == GameStatus.WIN; + } + + public boolean isLoseStatus() { + return gameStatus == GameStatus.LOSE; + } } diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index ed13b6de8..a57b45476 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -21,26 +21,23 @@ public class Minesweeper implements GameInitializable, GameRunnable { // 입출력에 대한건 여기서! private final InputHandler inputHandler; private final OutputHandler outputHandler; - private GameStatus gameStatus; public Minesweeper(GameConfig gameConfig) { gameBoard = new GameBoard(gameConfig.getGameLevel()); this.inputHandler = gameConfig.getInputHandler(); this.outputHandler = gameConfig.getOutputHandler(); - gameStatus = GameStatus.IN_PROGRESS; } @Override public void initialize() { gameBoard.initializeGame(); - gameStatus = GameStatus.IN_PROGRESS; } @Override public void run() { outputHandler.showGameStartCommand(); - while (GameStatus.IN_PROGRESS == gameStatus) { + while (gameBoard.isInProgress()) { try { outputHandler.showBoard(gameBoard); @@ -60,10 +57,10 @@ public void run() { outputHandler.showBoard(gameBoard); // 마지막 결과값 보여줌 - if (doesUserWinTheGame()) { + if (gameBoard.isWinStatus()) { outputHandler.showGameWinningComment(); } - if (doesUserLoseTheGame()) { + if (gameBoard.isLoseStatus()) { outputHandler.showGameLosingComment(); } } @@ -72,30 +69,17 @@ private void actOnCell(CellPosition cellPosition, UserAction userAction) { if (doesUserChooseToPlantFlag(userAction)) { gameBoard.flagAt(cellPosition); - checkIfGameIsOver(); return; } if (doesUserChooseToOpenCell(userAction)) { - if (gameBoard.isLandMineCellAt(cellPosition)) { - gameBoard.openAt(cellPosition); - changeGameStatusToLose(); - return; - } - - gameBoard.openSurroundedCells(cellPosition); - checkIfGameIsOver(); - return; + gameBoard.openAt(cellPosition); } System.out.println("잘못된 번호를 선택하셨습니다."); } - private void changeGameStatusToLose() { - gameStatus = GameStatus.LOSE; - } - private boolean doesUserChooseToOpenCell(UserAction userAction) { return userAction == UserAction.OPEN; } @@ -121,24 +105,6 @@ private CellPosition getCellInputFromUser() { return cellPosition; } - private boolean doesUserLoseTheGame() { - return gameStatus == GameStatus.LOSE; - } - - private boolean doesUserWinTheGame() { - return gameStatus == GameStatus.WIN; - } - - private void checkIfGameIsOver() { - if (gameBoard.isAllCellChecked()) { // 게임 이긴 것 - changeGameStatusToWin(); - } - } - - private void changeGameStatusToWin() { - gameStatus = GameStatus.WIN; - } - // private boolean isAllCellOpened() { // boolean isAllOpened = true; // for (int row = 0; row < BOARD_ROW_SIZE; row++) { From 0d92db4a398e2f4d2b61a27cbdc7851f6b4f0df9 Mon Sep 17 00:00:00 2001 From: devny Date: Mon, 17 Mar 2025 00:28:00 +0900 Subject: [PATCH 36/39] =?UTF-8?q?2025=2003=2017=20by=20devny=20-=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=EC=99=80=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EC=9D=98=20=EB=82=98=EC=97=B4=20=EC=88=9C=EC=84=9C=20+=20Input?= =?UTF-8?q?Handler=20git=20=EC=B6=94=EA=B0=80(=EB=B9=A0=EC=A7=84=EA=B1=B0)?= =?UTF-8?q?=20=20+=20public=20=EB=A8=BC=EC=A0=80,=20private=20?= =?UTF-8?q?=EB=82=98=EC=A4=91=20+=20=EA=B7=B8=EB=A3=B9=ED=99=94=ED=95=B4?= =?UTF-8?q?=EC=84=9C=20=EB=B9=84=EC=8A=B7=ED=95=9C=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=93=A4=EB=81=BC=EB=A6=AC=20=EB=B0=B0=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleancode/minesweeper/tobe/GameBoard.java | 143 +++++++++--------- .../minesweeper/tobe/Minesweeper.java | 38 ++--- .../tobe/io/ConsoleInputHandler.java | 4 +- .../minesweeper/tobe/io/InputHandler.java | 11 ++ 4 files changed, 104 insertions(+), 92 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index 147a6de5f..c9fe5b39a 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -23,28 +23,40 @@ public GameBoard(GameLevel gameLevel) { initializeGameStatus(); } - public void flagAt(CellPosition cellPosition) { - Cell cell = findCell(cellPosition); - cell.flag(); + // 상태 변경 + public void initializeGame() { + initializeGameStatus(); + CellPositions cellPositions = CellPositions.from(board); - checkIfGameIsOver(); + initializeEmptyCells(cellPositions); + List landMinePositions = cellPositions.extractRandomPositions(landMineCount); + initializeLandMineCells(landMinePositions); + + List numberPositionCandidates = cellPositions.subtract(landMinePositions); // 너가 가진 거에서 파라미터가 주어진 것을 뺀 나머지 position 을 줘 + initializeNumberCells(numberPositionCandidates); } - private void checkIfGameIsOver() { - if (isAllCellChecked()) { // 게임 이긴 것 - changeGameStatusToWin(); + + public void openAt(CellPosition cellPosition) { + if (isLandMineCellAt(cellPosition)) { + openOneCell(cellPosition); + changeGameStatusToLose(); + return; } - } - private void changeGameStatusToWin() { - gameStatus = GameStatus.WIN; + openSurroundedCells(cellPosition); + checkIfGameIsOver(); + return; } - public boolean isLandMineCellAt(CellPosition cellPosition) { + public void flagAt(CellPosition cellPosition) { // Cell 의 상태 변경 Cell cell = findCell(cellPosition); - return cell.isLandMine(); + cell.flag(); + + checkIfGameIsOver(); } + // 판별 public boolean isInvalidCellPosition(CellPosition cellPosition) { int rowSize = getRowSize(); int colSize = getColSize(); @@ -52,25 +64,33 @@ public boolean isInvalidCellPosition(CellPosition cellPosition) { return cellPosition.isRowIndexMoreThanOrEqual(rowSize) || cellPosition.isColIndexMoreThanOrEqual(colSize); } + public boolean isInProgress() { + return gameStatus == GameStatus.IN_PROGRESS; + } + + public boolean isWinStatus() { + return gameStatus == GameStatus.WIN; + } + + public boolean isLoseStatus() { + return gameStatus == GameStatus.LOSE; + } + // 조회 public CellSnapshot getSnapshot(CellPosition cellPosition) { Cell cell = findCell(cellPosition); return cell.getSnapshot(); } - public void initializeGame() { - initializeGameStatus(); - CellPositions cellPositions = CellPositions.from(board); - - initializeEmptyCells(cellPositions); - - List landMinePositions = cellPositions.extractRandomPositions(landMineCount); - initializeLandMineCells(landMinePositions); + public int getRowSize() { + return board.length; + } - List numberPositionCandidates = cellPositions.subtract(landMinePositions); // 너가 가진 거에서 파라미터가 주어진 것을 뺀 나머지 position 을 줘 - initializeNumberCells(numberPositionCandidates); + public int getColSize() { + return board[0].length; } + private void initializeGameStatus() { gameStatus = GameStatus.IN_PROGRESS; } @@ -97,10 +117,6 @@ private void initializeNumberCells(List numberPositionCandidates) } } - private void updateCellAt(CellPosition position, Cell cell) { - board[position.getRowIndex()][position.getColIndex()] = cell; - } - private int countNearbyLandMines(CellPosition cellPosition) { int rowSize = getRowSize(); int colSize = getColSize(); @@ -138,7 +154,6 @@ private int countNearbyLandMines(CellPosition cellPosition) { // } // return count; } - private List calculateSurroundedPositions(CellPosition cellPosition, int rowSize, int colSize) { return RelativePosition.SURROUNDED_POSITION.stream() .filter(relativePosition -> cellPosition.canCalculatePositionBy(relativePosition)) // relativePosition 이 계산 가능한 Position 이야? 0 이상이야? @@ -148,37 +163,16 @@ private List calculateSurroundedPositions(CellPosition cellPositio .toList(); } - public int getRowSize() { - return board.length; - } - - public boolean isAllCellChecked() { -// return Arrays.stream(board)// BOARD 라는 이중 string 배열에 stream 을 걸면 String[] 형태의 Stream 이 나옴 Stream -// // 그냥 Map 을 하면 Stream> 이 나오는데 flatMap 을 하면서 평탄화를 통해 이중배열을 배열로, 즉, Stream 으로 만들어주는 것 -// .flatMap(stringArr -> Arrays.stream(stringArr)) // flatMap 을 하면 Stream 이 생기는데 이 stringArray 를 하나씩 돌면서 다시 Stream 만들거다 -// // 여기까지가 Stream -// .allMatch(Cell::isChecked); - - // cell 을 가공하던 책임이 cells 안으로 들어가면서 목록을 구성하는 책임이 Cells 안으로 들어감 - Cells cells = Cells.from(board); - return cells.isAllChecked(); - - } - - private Cell findCell(CellPosition cellPosition) { - return board[cellPosition.getRowIndex()][cellPosition.getColIndex()]; - } - - public int getColSize() { - return board[0].length; + private void updateCellAt(CellPosition position, Cell cell) { + board[position.getRowIndex()][position.getColIndex()] = cell; } - public void openOneCell(CellPosition cellPosition) { + private void openOneCell(CellPosition cellPosition) { Cell cell = findCell(cellPosition); cell.open(); } - public void openSurroundedCells(CellPosition cellPosition) { + private void openSurroundedCells(CellPosition cellPosition) { if (cellPosition.isRowIndexMoreThanOrEqual(getRowSize()) || cellPosition.isColIndexMoreThanOrEqual(getColSize())) { // 얘도 바깥에서 BoardSize 보다 큰지는 확인했지만 재귀로 연산을 하기때문에 두어야 함. return; @@ -220,42 +214,49 @@ public void openSurroundedCells(CellPosition cellPosition) { // } } - - private boolean doesCellHaveLandMineCount(CellPosition cellPosition) { + private boolean isOpenedCell(CellPosition cellPosition) { Cell cell = findCell(cellPosition); - return cell.hasLandMineCount(); + return cell.isOpened(); } - private boolean isOpenedCell(CellPosition cellPosition) { + private boolean isLandMineCellAt(CellPosition cellPosition) { Cell cell = findCell(cellPosition); - return cell.isOpened(); + return cell.isLandMine(); } - public boolean isInProgress() { - return gameStatus == GameStatus.IN_PROGRESS; + private boolean doesCellHaveLandMineCount(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); + return cell.hasLandMineCount(); } - public void openAt(CellPosition cellPosition) { - if (isLandMineCellAt(cellPosition)) { - openOneCell(cellPosition); - changeGameStatusToLose(); - return; + private void checkIfGameIsOver() { + if (isAllCellChecked()) { // 게임 이긴 것 + changeGameStatusToWin(); } + } + + private boolean isAllCellChecked() { +// return Arrays.stream(board)// BOARD 라는 이중 string 배열에 stream 을 걸면 String[] 형태의 Stream 이 나옴 Stream +// // 그냥 Map 을 하면 Stream> 이 나오는데 flatMap 을 하면서 평탄화를 통해 이중배열을 배열로, 즉, Stream 으로 만들어주는 것 +// .flatMap(stringArr -> Arrays.stream(stringArr)) // flatMap 을 하면 Stream 이 생기는데 이 stringArray 를 하나씩 돌면서 다시 Stream 만들거다 +// // 여기까지가 Stream +// .allMatch(Cell::isChecked); + + // cell 을 가공하던 책임이 cells 안으로 들어가면서 목록을 구성하는 책임이 Cells 안으로 들어감 + Cells cells = Cells.from(board); + return cells.isAllChecked(); - openSurroundedCells(cellPosition); - checkIfGameIsOver(); - return; } private void changeGameStatusToLose() { gameStatus = GameStatus.LOSE; } - public boolean isWinStatus() { - return gameStatus == GameStatus.WIN; + private void changeGameStatusToWin() { + gameStatus = GameStatus.WIN; } - public boolean isLoseStatus() { - return gameStatus == GameStatus.LOSE; + private Cell findCell(CellPosition cellPosition) { + return board[cellPosition.getRowIndex()][cellPosition.getColIndex()]; } } diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index a57b45476..539a81042 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -65,6 +65,23 @@ public void run() { } } + private CellPosition getCellInputFromUser() { + outputHandler.showCommentForSelectingCell(); + CellPosition cellPosition = inputHandler.getCellPositionFromUser(); // 이때, index 로서의 기능은 할 수 있게 함. + // 보드 길이에 따른 Position 검증은 GameBoard 에! + // 한군데서 하면 되는데 너무 잘게 쪼개는거 아닌가요? -> 그렇게 느낄 수 있겠지만 책임을 조금 분리! + // 보드가 있는 곳에서 자연스럽게 검증을 해보자! + if (gameBoard.isInvalidCellPosition(cellPosition)) { + throw new GameException("잘못된 좌표를 선택하셨습니다."); + } + return cellPosition; + } + + private UserAction getUserActionInputFromUser() { + outputHandler.showCommentFOrUserAction(); + return inputHandler.getUserActionFromUser() ; + } + private void actOnCell(CellPosition cellPosition, UserAction userAction) { if (doesUserChooseToPlantFlag(userAction)) { @@ -80,29 +97,12 @@ private void actOnCell(CellPosition cellPosition, UserAction userAction) { System.out.println("잘못된 번호를 선택하셨습니다."); } - private boolean doesUserChooseToOpenCell(UserAction userAction) { - return userAction == UserAction.OPEN; - } - private boolean doesUserChooseToPlantFlag(UserAction userAction) { return userAction == UserAction.FLAG; } - private UserAction getUserActionInputFromUser() { - outputHandler.showCommentFOrUserAction(); - return inputHandler.getUserActionFromUser() ; - } - - private CellPosition getCellInputFromUser() { - outputHandler.showCommentForSelectingCell(); - CellPosition cellPosition = inputHandler.getCellPositionFromUser(); // 이때, index 로서의 기능은 할 수 있게 함. - // 보드 길이에 따른 Position 검증은 GameBoard 에! - // 한군데서 하면 되는데 너무 잘게 쪼개는거 아닌가요? -> 그렇게 느낄 수 있겠지만 책임을 조금 분리! - // 보드가 있는 곳에서 자연스럽게 검증을 해보자! - if (gameBoard.isInvalidCellPosition(cellPosition)) { - throw new GameException("잘못된 좌표를 선택하셨습니다."); - } - return cellPosition; + private boolean doesUserChooseToOpenCell(UserAction userAction) { + return userAction == UserAction.OPEN; } // private boolean isAllCellOpened() { diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java index b4c658fa9..ae5c3d1b0 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java @@ -29,9 +29,9 @@ public UserAction getUserActionFromUser() { @Override public CellPosition getCellPositionFromUser() { String userInput = SCANNER.nextLine(); - int colIndex = boardIndexConverter.getSelectedColIndex(userInput); - int rowIndex = boardIndexConverter.getSelectedRowIndex(userInput); + int rowIndex = boardIndexConverter.getSelectedRowIndex(userInput); + int colIndex = boardIndexConverter.getSelectedColIndex(userInput); return CellPosition.of(rowIndex, colIndex); } diff --git a/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java new file mode 100644 index 000000000..05271ecee --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java @@ -0,0 +1,11 @@ +package cleancode.minesweeper.tobe.io; + +import cleancode.minesweeper.tobe.position.CellPosition; +import cleancode.minesweeper.tobe.user.UserAction; + +public interface InputHandler { + + UserAction getUserActionFromUser(); + + CellPosition getCellPositionFromUser(); +} From c7c093d89dd67504083b56c32b712e6635e61c5a Mon Sep 17 00:00:00 2001 From: devny Date: Mon, 17 Mar 2025 01:17:56 +0900 Subject: [PATCH 37/39] =?UTF-8?q?2025=2003=2017=20by=20devny=20-=20package?= =?UTF-8?q?=20=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleancode/minesweeper/tobe/GameApplication.java | 12 ++++-------- .../cleancode/minesweeper/tobe/Minesweeper.java | 13 ++++++++----- .../cleancode/minesweeper/tobe/io/InputHandler.java | 11 ----------- .../tobe/{ => minesweeper/board}/GameBoard.java | 12 ++++++------ .../tobe/{ => minesweeper/board}/GameStatus.java | 2 +- .../tobe/{ => minesweeper/board}/cell/Cell.java | 2 +- .../{ => minesweeper/board}/cell/CellSnapshot.java | 2 +- .../board}/cell/CellSnapshotStatus.java | 2 +- .../{ => minesweeper/board}/cell/CellState.java | 2 +- .../tobe/{ => minesweeper/board}/cell/Cells.java | 2 +- .../{ => minesweeper/board}/cell/EmptyCell.java | 2 +- .../{ => minesweeper/board}/cell/LandMineCell.java | 2 +- .../{ => minesweeper/board}/cell/NumberCell.java | 2 +- .../board}/position/CellPosition.java | 2 +- .../board}/position/CellPositions.java | 4 ++-- .../board}/position/RelativePosition.java | 2 +- .../tobe/{ => minesweeper}/config/GameConfig.java | 8 ++++---- .../{ => minesweeper/exception}/GameException.java | 2 +- .../tobe/{ => minesweeper}/gamelevel/Advanced.java | 2 +- .../tobe/{ => minesweeper}/gamelevel/Beginner.java | 2 +- .../tobe/{ => minesweeper}/gamelevel/GameLevel.java | 2 +- .../tobe/{ => minesweeper}/gamelevel/Middle.java | 2 +- .../{ => minesweeper}/gamelevel/VeryBeginner.java | 2 +- .../{ => minesweeper/io}/BoardIndexConverter.java | 4 +++- .../{ => minesweeper}/io/ConsoleInputHandler.java | 7 +++---- .../{ => minesweeper}/io/ConsoleOutputHandler.java | 13 +++++++------ .../tobe/minesweeper/io/InputHandler.java | 11 +++++++++++ .../tobe/{ => minesweeper}/io/OutputHandler.java | 6 +++--- .../{ => minesweeper}/io/sign/CellSignFinder.java | 4 ++-- .../io/sign/CellSignProvidable.java | 4 ++-- .../{ => minesweeper}/io/sign/CellSignProvider.java | 7 +++---- .../io/sign/EmptyCellSignProvider.java | 6 +++--- .../io/sign/FlagCellSignProvider.java | 6 +++--- .../io/sign/LandMineCellSignProvider.java | 6 +++--- .../io/sign/NumberCellSignProvider.java | 6 +++--- .../io/sign/UncheckedCellSignProvider.java | 6 +++--- .../tobe/{ => minesweeper}/user/UserAction.java | 2 +- 37 files changed, 91 insertions(+), 91 deletions(-) delete mode 100644 src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/GameBoard.java (95%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/GameStatus.java (80%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/cell/Cell.java (81%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/cell/CellSnapshot.java (96%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/cell/CellSnapshotStatus.java (82%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/cell/CellState.java (93%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/cell/Cells.java (93%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/cell/EmptyCell.java (93%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/cell/LandMineCell.java (93%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/cell/NumberCell.java (94%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/position/CellPosition.java (97%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/position/CellPositions.java (94%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/position/RelativePosition.java (95%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/config/GameConfig.java (71%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/exception}/GameException.java (69%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/gamelevel/Advanced.java (85%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/gamelevel/Beginner.java (85%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/gamelevel/GameLevel.java (64%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/gamelevel/Middle.java (85%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/gamelevel/VeryBeginner.java (85%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/io}/BoardIndexConverter.java (90%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/ConsoleInputHandler.java (81%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/ConsoleOutputHandler.java (85%) create mode 100644 src/main/java/cleancode/minesweeper/tobe/minesweeper/io/InputHandler.java rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/OutputHandler.java (65%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/sign/CellSignFinder.java (87%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/sign/CellSignProvidable.java (68%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/sign/CellSignProvider.java (91%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/sign/EmptyCellSignProvider.java (64%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/sign/FlagCellSignProvider.java (62%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/sign/LandMineCellSignProvider.java (65%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/sign/NumberCellSignProvider.java (64%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/sign/UncheckedCellSignProvider.java (64%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/user/UserAction.java (81%) diff --git a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java index d24355748..7be464352 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java @@ -1,13 +1,9 @@ package cleancode.minesweeper.tobe; -import cleancode.minesweeper.tobe.config.GameConfig; -import cleancode.minesweeper.tobe.gamelevel.Advanced; -import cleancode.minesweeper.tobe.gamelevel.Beginner; -import cleancode.minesweeper.tobe.gamelevel.GameLevel; -import cleancode.minesweeper.tobe.io.ConsoleInputHandler; -import cleancode.minesweeper.tobe.io.ConsoleOutputHandler; -import cleancode.minesweeper.tobe.io.InputHandler; -import cleancode.minesweeper.tobe.io.OutputHandler; +import cleancode.minesweeper.tobe.minesweeper.config.GameConfig; +import cleancode.minesweeper.tobe.minesweeper.gamelevel.Beginner; +import cleancode.minesweeper.tobe.minesweeper.io.ConsoleInputHandler; +import cleancode.minesweeper.tobe.minesweeper.io.ConsoleOutputHandler; public class GameApplication { diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index 539a81042..77670ea00 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -1,12 +1,15 @@ package cleancode.minesweeper.tobe; -import cleancode.minesweeper.tobe.config.GameConfig; +import cleancode.minesweeper.tobe.minesweeper.board.GameBoard; +import cleancode.minesweeper.tobe.minesweeper.config.GameConfig; +import cleancode.minesweeper.tobe.minesweeper.exception.GameException; import cleancode.minesweeper.tobe.game.GameInitializable; import cleancode.minesweeper.tobe.game.GameRunnable; -import cleancode.minesweeper.tobe.io.InputHandler; -import cleancode.minesweeper.tobe.io.OutputHandler; -import cleancode.minesweeper.tobe.position.CellPosition; -import cleancode.minesweeper.tobe.user.UserAction; +import cleancode.minesweeper.tobe.minesweeper.io.BoardIndexConverter; +import cleancode.minesweeper.tobe.minesweeper.io.InputHandler; +import cleancode.minesweeper.tobe.minesweeper.io.OutputHandler; +import cleancode.minesweeper.tobe.minesweeper.board.position.CellPosition; +import cleancode.minesweeper.tobe.minesweeper.user.UserAction; // 모든 지뢰찾기 게임 로직을 여기에 둘 것임 public class Minesweeper implements GameInitializable, GameRunnable { diff --git a/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java deleted file mode 100644 index 05271ecee..000000000 --- a/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java +++ /dev/null @@ -1,11 +0,0 @@ -package cleancode.minesweeper.tobe.io; - -import cleancode.minesweeper.tobe.position.CellPosition; -import cleancode.minesweeper.tobe.user.UserAction; - -public interface InputHandler { - - UserAction getUserActionFromUser(); - - CellPosition getCellPositionFromUser(); -} diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java similarity index 95% rename from src/main/java/cleancode/minesweeper/tobe/GameBoard.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java index c9fe5b39a..1b51a47fb 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java @@ -1,10 +1,10 @@ -package cleancode.minesweeper.tobe; +package cleancode.minesweeper.tobe.minesweeper.board; -import cleancode.minesweeper.tobe.cell.*; -import cleancode.minesweeper.tobe.gamelevel.GameLevel; -import cleancode.minesweeper.tobe.position.CellPosition; -import cleancode.minesweeper.tobe.position.CellPositions; -import cleancode.minesweeper.tobe.position.RelativePosition; +import cleancode.minesweeper.tobe.minesweeper.gamelevel.GameLevel; +import cleancode.minesweeper.tobe.minesweeper.board.position.CellPosition; +import cleancode.minesweeper.tobe.minesweeper.board.position.CellPositions; +import cleancode.minesweeper.tobe.minesweeper.board.position.RelativePosition; +import cleancode.minesweeper.tobe.minesweeper.board.cell.*; import java.util.List; diff --git a/src/main/java/cleancode/minesweeper/tobe/GameStatus.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameStatus.java similarity index 80% rename from src/main/java/cleancode/minesweeper/tobe/GameStatus.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameStatus.java index f07e75d8b..28afa3328 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameStatus.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameStatus.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe; +package cleancode.minesweeper.tobe.minesweeper.board; public enum GameStatus { diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/Cell.java similarity index 81% rename from src/main/java/cleancode/minesweeper/tobe/cell/Cell.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/Cell.java index 8f3e91aa9..6c161f7b5 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/Cell.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.cell; +package cleancode.minesweeper.tobe.minesweeper.board.cell; public interface Cell { diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshot.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellSnapshot.java similarity index 96% rename from src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshot.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellSnapshot.java index 6d4a5f83b..1fd6843fc 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshot.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellSnapshot.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.cell; +package cleancode.minesweeper.tobe.minesweeper.board.cell; import java.util.Objects; diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshotStatus.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellSnapshotStatus.java similarity index 82% rename from src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshotStatus.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellSnapshotStatus.java index 54008afdd..a634b978f 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshotStatus.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellSnapshotStatus.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.cell; +package cleancode.minesweeper.tobe.minesweeper.board.cell; public enum CellSnapshotStatus { diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/CellState.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellState.java similarity index 93% rename from src/main/java/cleancode/minesweeper/tobe/cell/CellState.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellState.java index 793f5d7c2..17b21af2c 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/CellState.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellState.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.cell; +package cleancode.minesweeper.tobe.minesweeper.board.cell; public class CellState { private boolean isFlagged; diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/Cells.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/Cells.java similarity index 93% rename from src/main/java/cleancode/minesweeper/tobe/cell/Cells.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/Cells.java index bb0832a5e..01b98dd86 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/Cells.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/Cells.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.cell; +package cleancode.minesweeper.tobe.minesweeper.board.cell; import java.util.Arrays; import java.util.List; diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/EmptyCell.java similarity index 93% rename from src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/EmptyCell.java index 4f6f36eb0..b3d9a86f5 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/EmptyCell.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.cell; +package cleancode.minesweeper.tobe.minesweeper.board.cell; public class EmptyCell implements Cell { private final CellState cellState = CellState.initialize(); diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCell.java similarity index 93% rename from src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCell.java index d081e37d3..9f88406d7 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCell.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.cell; +package cleancode.minesweeper.tobe.minesweeper.board.cell; public class LandMineCell implements Cell { private final CellState cellState = CellState.initialize(); diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/NumberCell.java similarity index 94% rename from src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/NumberCell.java index 897956854..cd181e6b2 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/NumberCell.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.cell; +package cleancode.minesweeper.tobe.minesweeper.board.cell; public class NumberCell implements Cell { private final int nearbyLandMineCount; // 주변 지뢰 수 diff --git a/src/main/java/cleancode/minesweeper/tobe/position/CellPosition.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/position/CellPosition.java similarity index 97% rename from src/main/java/cleancode/minesweeper/tobe/position/CellPosition.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/position/CellPosition.java index d40c8283c..236e935de 100644 --- a/src/main/java/cleancode/minesweeper/tobe/position/CellPosition.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/position/CellPosition.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.position; +package cleancode.minesweeper.tobe.minesweeper.board.position; import java.util.Objects; diff --git a/src/main/java/cleancode/minesweeper/tobe/position/CellPositions.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/position/CellPositions.java similarity index 94% rename from src/main/java/cleancode/minesweeper/tobe/position/CellPositions.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/position/CellPositions.java index 892286ddc..af3405869 100644 --- a/src/main/java/cleancode/minesweeper/tobe/position/CellPositions.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/position/CellPositions.java @@ -1,6 +1,6 @@ -package cleancode.minesweeper.tobe.position; +package cleancode.minesweeper.tobe.minesweeper.board.position; -import cleancode.minesweeper.tobe.cell.Cell; +import cleancode.minesweeper.tobe.minesweeper.board.cell.Cell; import java.util.ArrayList; import java.util.Collections; diff --git a/src/main/java/cleancode/minesweeper/tobe/position/RelativePosition.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/position/RelativePosition.java similarity index 95% rename from src/main/java/cleancode/minesweeper/tobe/position/RelativePosition.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/position/RelativePosition.java index afbf36bd7..070ae9264 100644 --- a/src/main/java/cleancode/minesweeper/tobe/position/RelativePosition.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/position/RelativePosition.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.position; +package cleancode.minesweeper.tobe.minesweeper.board.position; import java.util.List; import java.util.Objects; diff --git a/src/main/java/cleancode/minesweeper/tobe/config/GameConfig.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/config/GameConfig.java similarity index 71% rename from src/main/java/cleancode/minesweeper/tobe/config/GameConfig.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/config/GameConfig.java index 5238acbbc..0fdbb8ed8 100644 --- a/src/main/java/cleancode/minesweeper/tobe/config/GameConfig.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/config/GameConfig.java @@ -1,8 +1,8 @@ -package cleancode.minesweeper.tobe.config; +package cleancode.minesweeper.tobe.minesweeper.config; -import cleancode.minesweeper.tobe.gamelevel.GameLevel; -import cleancode.minesweeper.tobe.io.InputHandler; -import cleancode.minesweeper.tobe.io.OutputHandler; +import cleancode.minesweeper.tobe.minesweeper.gamelevel.GameLevel; +import cleancode.minesweeper.tobe.minesweeper.io.InputHandler; +import cleancode.minesweeper.tobe.minesweeper.io.OutputHandler; public class GameConfig { diff --git a/src/main/java/cleancode/minesweeper/tobe/GameException.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/exception/GameException.java similarity index 69% rename from src/main/java/cleancode/minesweeper/tobe/GameException.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/exception/GameException.java index c0e34c4ac..e63c9cd8b 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameException.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/exception/GameException.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe; +package cleancode.minesweeper.tobe.minesweeper.exception; public class GameException extends RuntimeException { public GameException(String message) { diff --git a/src/main/java/cleancode/minesweeper/tobe/gamelevel/Advanced.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/Advanced.java similarity index 85% rename from src/main/java/cleancode/minesweeper/tobe/gamelevel/Advanced.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/Advanced.java index 4d378572d..ba858300a 100644 --- a/src/main/java/cleancode/minesweeper/tobe/gamelevel/Advanced.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/Advanced.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.gamelevel; +package cleancode.minesweeper.tobe.minesweeper.gamelevel; public class Advanced implements GameLevel { // GameLevel(추상화된 클래스) 의 Spec 을 만족시키는 구현체 @Override diff --git a/src/main/java/cleancode/minesweeper/tobe/gamelevel/Beginner.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/Beginner.java similarity index 85% rename from src/main/java/cleancode/minesweeper/tobe/gamelevel/Beginner.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/Beginner.java index 623531557..c695191fd 100644 --- a/src/main/java/cleancode/minesweeper/tobe/gamelevel/Beginner.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/Beginner.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.gamelevel; +package cleancode.minesweeper.tobe.minesweeper.gamelevel; public class Beginner implements GameLevel { // GameLevel(추상화된 클래스) 의 Spec 을 만족시키는 구현체 @Override diff --git a/src/main/java/cleancode/minesweeper/tobe/gamelevel/GameLevel.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/GameLevel.java similarity index 64% rename from src/main/java/cleancode/minesweeper/tobe/gamelevel/GameLevel.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/GameLevel.java index ef4d7121b..c04c014f3 100644 --- a/src/main/java/cleancode/minesweeper/tobe/gamelevel/GameLevel.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/GameLevel.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.gamelevel; +package cleancode.minesweeper.tobe.minesweeper.gamelevel; public interface GameLevel { int getRowSize(); diff --git a/src/main/java/cleancode/minesweeper/tobe/gamelevel/Middle.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/Middle.java similarity index 85% rename from src/main/java/cleancode/minesweeper/tobe/gamelevel/Middle.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/Middle.java index 2a4217167..cdd265286 100644 --- a/src/main/java/cleancode/minesweeper/tobe/gamelevel/Middle.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/Middle.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.gamelevel; +package cleancode.minesweeper.tobe.minesweeper.gamelevel; public class Middle implements GameLevel { // GameLevel(추상화된 클래스) 의 Spec 을 만족시키는 구현체 @Override diff --git a/src/main/java/cleancode/minesweeper/tobe/gamelevel/VeryBeginner.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/VeryBeginner.java similarity index 85% rename from src/main/java/cleancode/minesweeper/tobe/gamelevel/VeryBeginner.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/VeryBeginner.java index 470f697b8..f4aa50443 100644 --- a/src/main/java/cleancode/minesweeper/tobe/gamelevel/VeryBeginner.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/VeryBeginner.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.gamelevel; +package cleancode.minesweeper.tobe.minesweeper.gamelevel; public class VeryBeginner implements GameLevel { // GameLevel(추상화된 클래스) 의 Spec 을 만족시키는 구현체 @Override diff --git a/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/BoardIndexConverter.java similarity index 90% rename from src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/BoardIndexConverter.java index 01f966ec0..0b5766da9 100644 --- a/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/BoardIndexConverter.java @@ -1,4 +1,6 @@ -package cleancode.minesweeper.tobe; +package cleancode.minesweeper.tobe.minesweeper.io; + +import cleancode.minesweeper.tobe.minesweeper.exception.GameException; public class BoardIndexConverter { diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/ConsoleInputHandler.java similarity index 81% rename from src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/ConsoleInputHandler.java index ae5c3d1b0..77a7b22bd 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/ConsoleInputHandler.java @@ -1,8 +1,7 @@ -package cleancode.minesweeper.tobe.io; +package cleancode.minesweeper.tobe.minesweeper.io; -import cleancode.minesweeper.tobe.BoardIndexConverter; -import cleancode.minesweeper.tobe.position.CellPosition; -import cleancode.minesweeper.tobe.user.UserAction; +import cleancode.minesweeper.tobe.minesweeper.board.position.CellPosition; +import cleancode.minesweeper.tobe.minesweeper.user.UserAction; import java.util.Scanner; diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/ConsoleOutputHandler.java similarity index 85% rename from src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/ConsoleOutputHandler.java index 530af3a07..b5ed2bfac 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/ConsoleOutputHandler.java @@ -1,10 +1,11 @@ -package cleancode.minesweeper.tobe.io; +package cleancode.minesweeper.tobe.minesweeper.io; -import cleancode.minesweeper.tobe.GameBoard; -import cleancode.minesweeper.tobe.GameException; -import cleancode.minesweeper.tobe.cell.CellSnapshot; -import cleancode.minesweeper.tobe.io.sign.*; -import cleancode.minesweeper.tobe.position.CellPosition; +import cleancode.minesweeper.tobe.minesweeper.board.GameBoard; +import cleancode.minesweeper.tobe.minesweeper.exception.GameException; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshot; +import cleancode.minesweeper.tobe.minesweeper.board.position.CellPosition; +import cleancode.minesweeper.tobe.minesweeper.io.sign.CellSignFinder; +import cleancode.minesweeper.tobe.minesweeper.io.sign.CellSignProvider; import java.util.List; import java.util.stream.IntStream; diff --git a/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/InputHandler.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/InputHandler.java new file mode 100644 index 000000000..de67183e8 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/InputHandler.java @@ -0,0 +1,11 @@ +package cleancode.minesweeper.tobe.minesweeper.io; + +import cleancode.minesweeper.tobe.minesweeper.board.position.CellPosition; +import cleancode.minesweeper.tobe.minesweeper.user.UserAction; + +public interface InputHandler { + + UserAction getUserActionFromUser(); + + CellPosition getCellPositionFromUser(); +} diff --git a/src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/OutputHandler.java similarity index 65% rename from src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/OutputHandler.java index 49b800e2a..95703d630 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/OutputHandler.java @@ -1,7 +1,7 @@ -package cleancode.minesweeper.tobe.io; +package cleancode.minesweeper.tobe.minesweeper.io; -import cleancode.minesweeper.tobe.GameBoard; -import cleancode.minesweeper.tobe.GameException; +import cleancode.minesweeper.tobe.minesweeper.board.GameBoard; +import cleancode.minesweeper.tobe.minesweeper.exception.GameException; public interface OutputHandler { diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignFinder.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/CellSignFinder.java similarity index 87% rename from src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignFinder.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/CellSignFinder.java index 9e6de2ac7..6a3bac655 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignFinder.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/CellSignFinder.java @@ -1,6 +1,6 @@ -package cleancode.minesweeper.tobe.io.sign; +package cleancode.minesweeper.tobe.minesweeper.io.sign; -import cleancode.minesweeper.tobe.cell.CellSnapshot; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshot; import java.util.List; diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvidable.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/CellSignProvidable.java similarity index 68% rename from src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvidable.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/CellSignProvidable.java index 6ce273dcb..c2000aa20 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvidable.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/CellSignProvidable.java @@ -1,6 +1,6 @@ -package cleancode.minesweeper.tobe.io.sign; +package cleancode.minesweeper.tobe.minesweeper.io.sign; -import cleancode.minesweeper.tobe.cell.CellSnapshot; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshot; // cellsnapshot 을 넘겨줬을때 sign 을 return 하는 함수 public interface CellSignProvidable { diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvider.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/CellSignProvider.java similarity index 91% rename from src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvider.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/CellSignProvider.java index eeb0a5779..4d440b278 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvider.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/CellSignProvider.java @@ -1,10 +1,9 @@ -package cleancode.minesweeper.tobe.io.sign; +package cleancode.minesweeper.tobe.minesweeper.io.sign; -import cleancode.minesweeper.tobe.cell.CellSnapshot; -import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshot; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshotStatus; import java.util.Arrays; -import java.util.Optional; public enum CellSignProvider implements CellSignProvidable { diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/EmptyCellSignProvider.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/EmptyCellSignProvider.java similarity index 64% rename from src/main/java/cleancode/minesweeper/tobe/io/sign/EmptyCellSignProvider.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/EmptyCellSignProvider.java index e4056e312..6d04d475c 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/sign/EmptyCellSignProvider.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/EmptyCellSignProvider.java @@ -1,7 +1,7 @@ -package cleancode.minesweeper.tobe.io.sign; +package cleancode.minesweeper.tobe.minesweeper.io.sign; -import cleancode.minesweeper.tobe.cell.CellSnapshot; -import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshot; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshotStatus; public class EmptyCellSignProvider implements CellSignProvidable { protected static final String EMPTY_SIGN = "■"; diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/FlagCellSignProvider.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/FlagCellSignProvider.java similarity index 62% rename from src/main/java/cleancode/minesweeper/tobe/io/sign/FlagCellSignProvider.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/FlagCellSignProvider.java index 2953df35f..7fc8e4641 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/sign/FlagCellSignProvider.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/FlagCellSignProvider.java @@ -1,7 +1,7 @@ -package cleancode.minesweeper.tobe.io.sign; +package cleancode.minesweeper.tobe.minesweeper.io.sign; -import cleancode.minesweeper.tobe.cell.CellSnapshot; -import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshot; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshotStatus; public class FlagCellSignProvider implements CellSignProvidable { String FLAG_SIGN = "⚑"; diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/LandMineCellSignProvider.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/LandMineCellSignProvider.java similarity index 65% rename from src/main/java/cleancode/minesweeper/tobe/io/sign/LandMineCellSignProvider.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/LandMineCellSignProvider.java index 3529e4e28..19e20cf28 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/sign/LandMineCellSignProvider.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/LandMineCellSignProvider.java @@ -1,7 +1,7 @@ -package cleancode.minesweeper.tobe.io.sign; +package cleancode.minesweeper.tobe.minesweeper.io.sign; -import cleancode.minesweeper.tobe.cell.CellSnapshot; -import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshot; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshotStatus; public class LandMineCellSignProvider implements CellSignProvidable { private static final String LAND_MINE_SIGN = "☼"; diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/NumberCellSignProvider.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/NumberCellSignProvider.java similarity index 64% rename from src/main/java/cleancode/minesweeper/tobe/io/sign/NumberCellSignProvider.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/NumberCellSignProvider.java index 085fb2dca..251ee8aff 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/sign/NumberCellSignProvider.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/NumberCellSignProvider.java @@ -1,7 +1,7 @@ -package cleancode.minesweeper.tobe.io.sign; +package cleancode.minesweeper.tobe.minesweeper.io.sign; -import cleancode.minesweeper.tobe.cell.CellSnapshot; -import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshot; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshotStatus; public class NumberCellSignProvider implements CellSignProvidable { diff --git a/src/main/java/cleancode/minesweeper/tobe/io/sign/UncheckedCellSignProvider.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/UncheckedCellSignProvider.java similarity index 64% rename from src/main/java/cleancode/minesweeper/tobe/io/sign/UncheckedCellSignProvider.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/UncheckedCellSignProvider.java index 510524b8f..dd2cd143b 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/sign/UncheckedCellSignProvider.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/sign/UncheckedCellSignProvider.java @@ -1,7 +1,7 @@ -package cleancode.minesweeper.tobe.io.sign; +package cleancode.minesweeper.tobe.minesweeper.io.sign; -import cleancode.minesweeper.tobe.cell.CellSnapshot; -import cleancode.minesweeper.tobe.cell.CellSnapshotStatus; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshot; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshotStatus; public class UncheckedCellSignProvider implements CellSignProvidable { String UNCHECKED_SIGN = "□"; diff --git a/src/main/java/cleancode/minesweeper/tobe/user/UserAction.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/user/UserAction.java similarity index 81% rename from src/main/java/cleancode/minesweeper/tobe/user/UserAction.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/user/UserAction.java index 1c25e50ba..964a34d3c 100644 --- a/src/main/java/cleancode/minesweeper/tobe/user/UserAction.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/user/UserAction.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.user; +package cleancode.minesweeper.tobe.minesweeper.user; public enum UserAction { From 246660606effd131273d040474109790d2dc2012 Mon Sep 17 00:00:00 2001 From: devny Date: Tue, 18 Mar 2025 23:44:54 +0900 Subject: [PATCH 38/39] 2025 03 18 by devny - test commit --- src/test/java/cleancode/SampleTest.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/test/java/cleancode/SampleTest.java b/src/test/java/cleancode/SampleTest.java index fc43aa46e..34bad952a 100644 --- a/src/test/java/cleancode/SampleTest.java +++ b/src/test/java/cleancode/SampleTest.java @@ -1,22 +1,12 @@ package cleancode; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; class SampleTest { - @Test - void sample() { - // given - int a = 1; - int b = 2; - // when - int sum = a + b; - - // then - assertThat(sum).isEqualTo(3); - } } From d8e96f362aa4bab05297c07b24ef5f6eeac40ea4 Mon Sep 17 00:00:00 2001 From: devny Date: Wed, 19 Mar 2025 00:53:44 +0900 Subject: [PATCH 39/39] =?UTF-8?q?2025=2003=2018=20by=20devny=20-=20SampleT?= =?UTF-8?q?est.java=20=EC=97=90=20=ED=95=A8=EC=88=98=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/cleancode/SampleTest.java | 179 ++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/src/test/java/cleancode/SampleTest.java b/src/test/java/cleancode/SampleTest.java index 34bad952a..0300f00e1 100644 --- a/src/test/java/cleancode/SampleTest.java +++ b/src/test/java/cleancode/SampleTest.java @@ -1,12 +1,191 @@ package cleancode; +import cleancode.minesweeper.tobe.Minesweeper; +import cleancode.minesweeper.tobe.minesweeper.board.GameBoard; +import cleancode.minesweeper.tobe.minesweeper.board.GameStatus; +import cleancode.minesweeper.tobe.minesweeper.board.cell.*; +import cleancode.minesweeper.tobe.minesweeper.board.position.CellPosition; +import cleancode.minesweeper.tobe.minesweeper.config.GameConfig; +import cleancode.minesweeper.tobe.minesweeper.exception.GameException; +import cleancode.minesweeper.tobe.minesweeper.gamelevel.Advanced; +import cleancode.minesweeper.tobe.minesweeper.gamelevel.Beginner; +import cleancode.minesweeper.tobe.minesweeper.gamelevel.Middle; +import cleancode.minesweeper.tobe.minesweeper.gamelevel.VeryBeginner; +import cleancode.minesweeper.tobe.minesweeper.io.BoardIndexConverter; +import cleancode.minesweeper.tobe.minesweeper.io.ConsoleInputHandler; +import cleancode.minesweeper.tobe.minesweeper.io.ConsoleOutputHandler; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; + import static org.assertj.core.api.Assertions.assertThat; class SampleTest { + // ConsoleInputHandler.java + @DisplayName("Cell의 종류에 따라 적절한 상태로 설정되어있는지 확인한다.") + @Test + void checkSnapshot() { + // given + Cell cell = new EmptyCell(); + + CellSnapshot testCellSnapshot = null; + if (cell instanceof EmptyCell) { + testCellSnapshot = CellSnapshot.ofEmpty(); + } + if (cell instanceof NumberCell) { + testCellSnapshot = CellSnapshot.ofNumber(3); + } + if (cell instanceof LandMineCell) { + testCellSnapshot = CellSnapshot.ofLandMine(); + } + + // when + CellSnapshot cellSnapshot = cell.getSnapshot(); + + // then + assertThat(testCellSnapshot.getStatus()).isEqualTo(testCellSnapshot.getStatus()); + } + + // GameLevel + @DisplayName("레벨에 맞는 보드의 가로 크기를 가졌는지 확인한다.") + @Test + void getRowSize() { + // given + GameConfig gameConfig = new GameConfig( + new Advanced(), + new ConsoleInputHandler(), + new ConsoleOutputHandler() + ); + + int testRowSize = 0; + if (gameConfig.getGameLevel() instanceof VeryBeginner) { + testRowSize = 4; + } + if (gameConfig.getGameLevel() instanceof Beginner) { + testRowSize = 8; + } + if (gameConfig.getGameLevel() instanceof Middle) { + testRowSize = 14; + } + if (gameConfig.getGameLevel() instanceof Advanced) { + testRowSize = 20; + } + + + // when + int rowSize = gameConfig.getGameLevel().getRowSize(); + + // then + assertThat(testRowSize).isEqualTo(rowSize); + } + + // Advanced + @DisplayName("레벨에 맞는 보드의 세로 크기를 가졌는지 확인한다.") + @Test + void getColSize() { + // given + GameConfig gameConfig = new GameConfig( + new Advanced(), + new ConsoleInputHandler(), + new ConsoleOutputHandler() + ); + + + int testColSize = 0; + if (gameConfig.getGameLevel() instanceof VeryBeginner) { + testColSize = 5; + } + if (gameConfig.getGameLevel() instanceof Beginner) { + testColSize = 10; + } + if (gameConfig.getGameLevel() instanceof Middle) { + testColSize = 18; + } + if (gameConfig.getGameLevel() instanceof Advanced) { + testColSize = 24; + } + + // when + int colSize = gameConfig.getGameLevel().getColSize(); + + // then + assertThat(testColSize).isEqualTo(colSize); + } + + // Advanced + @DisplayName("레벨에 맞는 보드의 지뢰 갯수를 확인한다.") + @Test + void getLandMineCount() { + // given + GameConfig gameConfig = new GameConfig( + new Advanced(), + new ConsoleInputHandler(), + new ConsoleOutputHandler() + ); + + int testLandMineCount = 0; + if (gameConfig.getGameLevel() instanceof VeryBeginner) { + testLandMineCount = 2; + } + if (gameConfig.getGameLevel() instanceof Beginner) { + testLandMineCount = 10; + } + if (gameConfig.getGameLevel() instanceof Middle) { + testLandMineCount = 40; + } + if (gameConfig.getGameLevel() instanceof Advanced) { + testLandMineCount = 99; + } + + // when + int landMineCount = gameConfig.getGameLevel().getLandMineCount(); + + // then + assertThat(testLandMineCount).isEqualTo(landMineCount); + } + + @DisplayName("입력에 따른 행 변환이 의도에 맞게 되었는지 확인한다.") + @Test + void convertRowForm() { + // given + String userInput = "a5"; + int testRow = 4; + BoardIndexConverter boardIndexConverter = new BoardIndexConverter(); + + // when + int rowIndex = boardIndexConverter.getSelectedRowIndex(userInput); + + // then + assertThat(testRow).isEqualTo(rowIndex); + } + + @DisplayName("입력에 따른 열 변환이 의도에 맞게 되었는지 확인한다.") + @Test + void convertColForm() { + // given + String userInput = "a5"; + int testCol = 0; + BoardIndexConverter boardIndexConverter = new BoardIndexConverter(); + + // when + int colIndex = boardIndexConverter.getSelectedColIndex(userInput); + + // then + assertThat(testCol).isEqualTo(colIndex); + } + + @DisplayName("GameException 이 의도한 메시지를 보여주는지 확인한다.") + @Test + void checkGameException() { + // given + String testMessage = "test success"; + GameException gameException = new GameException(testMessage); + // when + String message = gameException.getMessage(); + // then + assertThat(testMessage).isEqualTo(message); + } }