From 37434cc9a116d0a2346918de9bc88151ef42a141 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Thu, 3 Oct 2024 22:37:28 +0900 Subject: [PATCH 01/30] =?UTF-8?q?feat(=EC=B6=94=EC=83=81):=20=EC=B6=94?= =?UTF-8?q?=EC=83=81=20-=20=EC=9D=B4=EB=A6=84=20=EC=A7=93=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/MinesweeperGame.java | 104 +++++++++--------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index dd85c3ce0..e3dad67d6 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -15,9 +15,9 @@ public static void main(String[] args) { System.out.println("지뢰찾기 게임 시작!"); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); Scanner scanner = new Scanner(System.in); - for (int i = 0; i < 8; i++) { - for (int j = 0; j < 10; j++) { - board[i][j] = "□"; + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 10; col++) { + board[row][col] = "□"; } } for (int i = 0; i < 10; i++) { @@ -25,38 +25,38 @@ public static void main(String[] args) { int row = new Random().nextInt(8); landMines[row][col] = true; } - for (int i = 0; i < 8; i++) { - for (int j = 0; j < 10; j++) { + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 10; col++) { int count = 0; - if (!landMines[i][j]) { - if (i - 1 >= 0 && j - 1 >= 0 && landMines[i - 1][j - 1]) { + if (!landMines[row][col]) { + if (row - 1 >= 0 && col - 1 >= 0 && landMines[row - 1][col - 1]) { count++; } - if (i - 1 >= 0 && landMines[i - 1][j]) { + if (row - 1 >= 0 && landMines[row - 1][col]) { count++; } - if (i - 1 >= 0 && j + 1 < 10 && landMines[i - 1][j + 1]) { + if (row - 1 >= 0 && col + 1 < 10 && landMines[row - 1][col + 1]) { count++; } - if (j - 1 >= 0 && landMines[i][j - 1]) { + if (col - 1 >= 0 && landMines[row][col - 1]) { count++; } - if (j + 1 < 10 && landMines[i][j + 1]) { + if (col + 1 < 10 && landMines[row][col + 1]) { count++; } - if (i + 1 < 8 && j - 1 >= 0 && landMines[i + 1][j - 1]) { + if (row + 1 < 8 && col - 1 >= 0 && landMines[row + 1][col - 1]) { count++; } - if (i + 1 < 8 && landMines[i + 1][j]) { + if (row + 1 < 8 && landMines[row + 1][col]) { count++; } - if (i + 1 < 8 && j + 1 < 10 && landMines[i + 1][j + 1]) { + if (row + 1 < 8 && col + 1 < 10 && landMines[row + 1][col + 1]) { count++; } - landMineCounts[i][j] = count; + landMineCounts[row][col] = count; continue; } - landMineCounts[i][j] = 0; + landMineCounts[row][col] = 0; } } while (true) { @@ -78,78 +78,78 @@ public static void main(String[] args) { } System.out.println(); System.out.println("선택할 좌표를 입력하세요. (예: a1)"); - String input = scanner.nextLine(); + String cellInput = 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) { + String userActionInput = scanner.nextLine(); + char cellInputCol = cellInput.charAt(0); + char cellInputRow = cellInput.charAt(1); + int selectedColumnIndex; + switch (cellInputCol) { case 'a': - col = 0; + selectedColumnIndex = 0; break; case 'b': - col = 1; + selectedColumnIndex = 1; break; case 'c': - col = 2; + selectedColumnIndex = 2; break; case 'd': - col = 3; + selectedColumnIndex = 3; break; case 'e': - col = 4; + selectedColumnIndex = 4; break; case 'f': - col = 5; + selectedColumnIndex = 5; break; case 'g': - col = 6; + selectedColumnIndex = 6; break; case 'h': - col = 7; + selectedColumnIndex = 7; break; case 'i': - col = 8; + selectedColumnIndex = 8; break; case 'j': - col = 9; + selectedColumnIndex = 9; break; default: - col = -1; + selectedColumnIndex = -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; + int selectedRowIndex = Character.getNumericValue(cellInputRow) - 1; + if (userActionInput.equals("2")) { + board[selectedRowIndex][selectedColumnIndex] = "⚑"; + boolean isAllOpened = true; + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 10; col++) { + if (board[row][col].equals("□")) { + isAllOpened = false; } } } - if (open) { + if (isAllOpened) { gameStatus = 1; } - } else if (input2.equals("1")) { - if (landMines[row][col]) { - board[row][col] = "☼"; + } else if (userActionInput.equals("1")) { + if (landMines[selectedRowIndex][selectedColumnIndex]) { + board[selectedRowIndex][selectedColumnIndex] = "☼"; gameStatus = -1; continue; } else { - open(row, col); + open(selectedRowIndex, selectedColumnIndex); } - boolean open = true; - for (int i = 0; i < 8; i++) { - for (int j = 0; j < 10; j++) { - if (board[i][j].equals("□")) { - open = false; + boolean isAllOpened = true; + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 10; col++) { + if (board[row][col].equals("□")) { + isAllOpened = false; } } } - if (open) { + if (isAllOpened) { gameStatus = 1; } } else { From abbc409edb2674ab7d600ca225e9e437ba26d35e Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Thu, 3 Oct 2024 22:52:18 +0900 Subject: [PATCH 02/30] =?UTF-8?q?feat(=EC=B6=94=EC=83=81):=20=EC=B6=94?= =?UTF-8?q?=EC=83=81=20-=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=84=A0?= =?UTF-8?q?=EC=96=B8=EB=B6=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/MinesweeperGame.java | 207 +++++++++--------- 1 file changed, 107 insertions(+), 100 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index e3dad67d6..38763c208 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -11,10 +11,108 @@ public class MinesweeperGame { private static int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 public static void main(String[] args) { - System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); - System.out.println("지뢰찾기 게임 시작!"); - System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + showGameStartComments(); Scanner scanner = new Scanner(System.in); + initializeGame(); + while (true) { + showBoard(); + 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 cellInput = scanner.nextLine(); + System.out.println("선택한 셀에 대한 행위를 선택하세요. (1: 오픈, 2: 깃발 꽂기)"); + String userActionInput = scanner.nextLine(); + char cellInputCol = cellInput.charAt(0); + char cellInputRow = cellInput.charAt(1); + int selectedColumnIndex = convertColFrom(cellInputCol); + int selectedRowIndex = convertRowFrom(cellInputRow); + if (userActionInput.equals("2")) { + board[selectedRowIndex][selectedColumnIndex] = "⚑"; + checkIfGameIsOver(); + } else if (userActionInput.equals("1")) { + if (landMines[selectedRowIndex][selectedColumnIndex]) { + board[selectedRowIndex][selectedColumnIndex] = "☼"; + gameStatus = -1; + continue; + } else { + open(selectedRowIndex, selectedColumnIndex); + } + checkIfGameIsOver(); + } else { + System.out.println("잘못된 번호를 선택하셨습니다."); + } + } + } + + private static void checkIfGameIsOver() { + boolean isAllOpened = isAllCellOpened(); + if (isAllOpened) { + gameStatus = 1; + } + } + + private static boolean isAllCellOpened() { + boolean isAllOpened = true; + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 10; col++) { + if (board[row][col].equals("□")) { + isAllOpened = false; + } + } + } + return isAllOpened; + } + + private static int convertRowFrom(char cellInputRow) { + return Character.getNumericValue(cellInputRow) - 1; + } + + private static int convertColFrom(char cellInputCol) { + 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; + } + } + + private static void showBoard() { + 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(); + } + } + + private static void initializeGame() { for (int row = 0; row < 8; row++) { for (int col = 0; col < 10; col++) { board[row][col] = "□"; @@ -59,103 +157,12 @@ public static void main(String[] args) { landMineCounts[row][col] = 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 cellInput = scanner.nextLine(); - System.out.println("선택한 셀에 대한 행위를 선택하세요. (1: 오픈, 2: 깃발 꽂기)"); - String userActionInput = scanner.nextLine(); - char cellInputCol = cellInput.charAt(0); - char cellInputRow = cellInput.charAt(1); - int selectedColumnIndex; - switch (cellInputCol) { - case 'a': - selectedColumnIndex = 0; - break; - case 'b': - selectedColumnIndex = 1; - break; - case 'c': - selectedColumnIndex = 2; - break; - case 'd': - selectedColumnIndex = 3; - break; - case 'e': - selectedColumnIndex = 4; - break; - case 'f': - selectedColumnIndex = 5; - break; - case 'g': - selectedColumnIndex = 6; - break; - case 'h': - selectedColumnIndex = 7; - break; - case 'i': - selectedColumnIndex = 8; - break; - case 'j': - selectedColumnIndex = 9; - break; - default: - selectedColumnIndex = -1; - break; - } - int selectedRowIndex = Character.getNumericValue(cellInputRow) - 1; - if (userActionInput.equals("2")) { - board[selectedRowIndex][selectedColumnIndex] = "⚑"; - boolean isAllOpened = true; - for (int row = 0; row < 8; row++) { - for (int col = 0; col < 10; col++) { - if (board[row][col].equals("□")) { - isAllOpened = false; - } - } - } - if (isAllOpened) { - gameStatus = 1; - } - } else if (userActionInput.equals("1")) { - if (landMines[selectedRowIndex][selectedColumnIndex]) { - board[selectedRowIndex][selectedColumnIndex] = "☼"; - gameStatus = -1; - continue; - } else { - open(selectedRowIndex, selectedColumnIndex); - } - boolean isAllOpened = true; - for (int row = 0; row < 8; row++) { - for (int col = 0; col < 10; col++) { - if (board[row][col].equals("□")) { - isAllOpened = false; - } - } - } - if (isAllOpened) { - gameStatus = 1; - } - } else { - System.out.println("잘못된 번호를 선택하셨습니다."); - } - } + } + + private static void showGameStartComments() { + System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + System.out.println("지뢰찾기 게임 시작!"); + System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); } private static void open(int row, int col) { From 138fbeba5f399f3beb54ce33e0ae719007c11d1e Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Fri, 4 Oct 2024 02:56:24 +0900 Subject: [PATCH 03/30] =?UTF-8?q?feat(=EC=B6=94=EC=83=81):=20=EC=B6=94?= =?UTF-8?q?=EC=83=81=20-=20=EC=B6=94=EC=83=81=ED=99=94=20=EB=A0=88?= =?UTF-8?q?=EB=B2=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/MinesweeperGame.java | 89 +++++++++++++------ 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index 38763c208..9dca39c5e 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -16,30 +16,25 @@ public static void main(String[] args) { initializeGame(); while (true) { showBoard(); - if (gameStatus == 1) { + if (doesUserWinTheGame()) { System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); break; } - if (gameStatus == -1) { + if (doesUserLoseTheGame()) { System.out.println("지뢰를 밟았습니다. GAME OVER!"); break; } - System.out.println(); - System.out.println("선택할 좌표를 입력하세요. (예: a1)"); - String cellInput = scanner.nextLine(); - System.out.println("선택한 셀에 대한 행위를 선택하세요. (1: 오픈, 2: 깃발 꽂기)"); - String userActionInput = scanner.nextLine(); - char cellInputCol = cellInput.charAt(0); - char cellInputRow = cellInput.charAt(1); - int selectedColumnIndex = convertColFrom(cellInputCol); - int selectedRowIndex = convertRowFrom(cellInputRow); - if (userActionInput.equals("2")) { + String cellInput = getCellInputFromUser(scanner); + String userActionInput = getUserActionInputFromUser(scanner); + int selectedColumnIndex = getSelectedColIndex(cellInput); + int selectedRowIndex = getSelectedRowIndex(cellInput); + if (doesUserChooseToPlantFlag(userActionInput)) { board[selectedRowIndex][selectedColumnIndex] = "⚑"; checkIfGameIsOver(); - } else if (userActionInput.equals("1")) { - if (landMines[selectedRowIndex][selectedColumnIndex]) { + } else if (doesUserChooseToOpenCell(userActionInput)) { + if (isLandMineCell(selectedRowIndex, selectedColumnIndex)) { board[selectedRowIndex][selectedColumnIndex] = "☼"; - gameStatus = -1; + changeGameStatusToLose(); continue; } else { open(selectedRowIndex, selectedColumnIndex); @@ -51,6 +46,50 @@ public static void main(String[] args) { } } + private static void changeGameStatusToLose() { + gameStatus = -1; + } + + private static boolean isLandMineCell(int selectedRowIndex, int selectedColumnIndex) { + return landMines[selectedRowIndex][selectedColumnIndex]; + } + + 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) { @@ -126,29 +165,29 @@ private static void initializeGame() { for (int row = 0; row < 8; row++) { for (int col = 0; col < 10; col++) { int count = 0; - if (!landMines[row][col]) { - if (row - 1 >= 0 && col - 1 >= 0 && landMines[row - 1][col - 1]) { + if (!isLandMineCell(row, col)) { + if (row - 1 >= 0 && col - 1 >= 0 && isLandMineCell(row - 1, col - 1)) { count++; } - if (row - 1 >= 0 && landMines[row - 1][col]) { + if (row - 1 >= 0 && isLandMineCell(row - 1, col)) { count++; } - if (row - 1 >= 0 && col + 1 < 10 && landMines[row - 1][col + 1]) { + if (row - 1 >= 0 && col + 1 < 10 && isLandMineCell(row - 1, col + 1)) { count++; } - if (col - 1 >= 0 && landMines[row][col - 1]) { + if (col - 1 >= 0 && isLandMineCell(row, col - 1)) { count++; } - if (col + 1 < 10 && landMines[row][col + 1]) { + if (col + 1 < 10 && isLandMineCell(row, col + 1)) { count++; } - if (row + 1 < 8 && col - 1 >= 0 && landMines[row + 1][col - 1]) { + if (row + 1 < 8 && col - 1 >= 0 && isLandMineCell(row + 1, col - 1)) { count++; } - if (row + 1 < 8 && landMines[row + 1][col]) { + if (row + 1 < 8 && isLandMineCell(row + 1, col)) { count++; } - if (row + 1 < 8 && col + 1 < 10 && landMines[row + 1][col + 1]) { + if (row + 1 < 8 && col + 1 < 10 && isLandMineCell(row + 1, col + 1)) { count++; } landMineCounts[row][col] = count; @@ -172,7 +211,7 @@ private static void open(int row, int col) { if (!board[row][col].equals("□")) { return; } - if (landMines[row][col]) { + if (isLandMineCell(row, col)) { return; } if (landMineCounts[row][col] != 0) { From 8479a0d6e72b81ddd876419863dc9241bdef5882 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Fri, 4 Oct 2024 19:05:42 +0900 Subject: [PATCH 04/30] =?UTF-8?q?feat(=EC=B6=94=EC=83=81):=20=EB=A7=A4?= =?UTF-8?q?=EC=A7=81=20=EB=84=98=EB=B2=84,=20=EB=A7=A4=EC=A7=81=20?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/MinesweeperGame.java | 70 +++++++++++-------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index 9dca39c5e..79431bee4 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -5,9 +5,17 @@ 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; + public static final int LAND_MINE_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 String[][] BOARD = new String[BOARD_ROW_SIZE][BOARD_COL_SIZE]; + private static Integer[][] NEARBY_LAND_MINE_COUNTS = new Integer[BOARD_ROW_SIZE][BOARD_COL_SIZE]; + private static boolean[][] LAND_MINES = new boolean[BOARD_ROW_SIZE][BOARD_COL_SIZE]; private static int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 public static void main(String[] args) { @@ -29,11 +37,11 @@ public static void main(String[] args) { int selectedColumnIndex = getSelectedColIndex(cellInput); int selectedRowIndex = getSelectedRowIndex(cellInput); if (doesUserChooseToPlantFlag(userActionInput)) { - board[selectedRowIndex][selectedColumnIndex] = "⚑"; + BOARD[selectedRowIndex][selectedColumnIndex] = FLAG_SIGN; checkIfGameIsOver(); } else if (doesUserChooseToOpenCell(userActionInput)) { if (isLandMineCell(selectedRowIndex, selectedColumnIndex)) { - board[selectedRowIndex][selectedColumnIndex] = "☼"; + BOARD[selectedRowIndex][selectedColumnIndex] = LAND_MINE_SIGN; changeGameStatusToLose(); continue; } else { @@ -51,7 +59,7 @@ private static void changeGameStatusToLose() { } private static boolean isLandMineCell(int selectedRowIndex, int selectedColumnIndex) { - return landMines[selectedRowIndex][selectedColumnIndex]; + return LAND_MINES[selectedRowIndex][selectedColumnIndex]; } private static boolean doesUserChooseToOpenCell(String userActionInput) { @@ -99,9 +107,9 @@ private static void checkIfGameIsOver() { private static boolean isAllCellOpened() { boolean isAllOpened = true; - for (int row = 0; row < 8; row++) { - for (int col = 0; col < 10; col++) { - if (board[row][col].equals("□")) { + 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; } } @@ -142,28 +150,28 @@ private static int convertColFrom(char cellInputCol) { private static void showBoard() { System.out.println(" a b c d e f g h i j"); - for (int i = 0; i < 8; i++) { + for (int i = 0; i < BOARD_ROW_SIZE; i++) { System.out.printf("%d ", i + 1); - for (int j = 0; j < 10; j++) { - System.out.print(board[i][j] + " "); + for (int j = 0; j < BOARD_COL_SIZE; j++) { + System.out.print(BOARD[i][j] + " "); } System.out.println(); } } private static void initializeGame() { - for (int row = 0; row < 8; row++) { - for (int col = 0; col < 10; col++) { - board[row][col] = "□"; + 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++) { + for (int i = 0; i < LAND_MINE_COUNT; i++) { int col = new Random().nextInt(10); int row = new Random().nextInt(8); - landMines[row][col] = true; + LAND_MINES[row][col] = true; } - for (int row = 0; row < 8; row++) { - for (int col = 0; col < 10; col++) { + 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)) { @@ -172,28 +180,28 @@ private static void initializeGame() { if (row - 1 >= 0 && isLandMineCell(row - 1, col)) { count++; } - if (row - 1 >= 0 && col + 1 < 10 && isLandMineCell(row - 1, col + 1)) { + 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 < 10 && isLandMineCell(row, col + 1)) { + if (col + 1 < BOARD_COL_SIZE && isLandMineCell(row, col + 1)) { count++; } - if (row + 1 < 8 && col - 1 >= 0 && isLandMineCell(row + 1, col - 1)) { + if (row + 1 < BOARD_ROW_SIZE && col - 1 >= 0 && isLandMineCell(row + 1, col - 1)) { count++; } - if (row + 1 < 8 && isLandMineCell(row + 1, col)) { + if (row + 1 < BOARD_ROW_SIZE && isLandMineCell(row + 1, col)) { count++; } - if (row + 1 < 8 && col + 1 < 10 && isLandMineCell(row + 1, col + 1)) { + if (row + 1 < BOARD_ROW_SIZE && col + 1 < BOARD_COL_SIZE && isLandMineCell(row + 1, col + 1)) { count++; } - landMineCounts[row][col] = count; + NEARBY_LAND_MINE_COUNTS[row][col] = count; continue; } - landMineCounts[row][col] = 0; + NEARBY_LAND_MINE_COUNTS[row][col] = 0; } } } @@ -205,20 +213,20 @@ private static void showGameStartComments() { } 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 (isLandMineCell(row, col)) { return; } - if (landMineCounts[row][col] != 0) { - board[row][col] = String.valueOf(landMineCounts[row][col]); + if (NEARBY_LAND_MINE_COUNTS[row][col] != 0) { + BOARD[row][col] = String.valueOf(NEARBY_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); From 44107dd2a0a01b25e11b10ba662c20a9a7612c34 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Fri, 4 Oct 2024 21:46:36 +0900 Subject: [PATCH 05/30] =?UTF-8?q?feat(=EB=85=BC=EB=A6=AC,-=EC=82=AC?= =?UTF-8?q?=EA=B3=A0=EC=9D=98-=ED=9D=90=EB=A6=84):=20[=EB=85=BC=EB=A6=AC,?= =?UTF-8?q?=20=EC=82=AC=EA=B3=A0=EC=9D=98=20=ED=9D=90=EB=A6=84]=20Early=20?= =?UTF-8?q?Return?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/MinesweeperGame.java | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index 79431bee4..515a62afa 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -34,24 +34,29 @@ public static void main(String[] args) { } String cellInput = getCellInputFromUser(scanner); String userActionInput = getUserActionInputFromUser(scanner); - int selectedColumnIndex = getSelectedColIndex(cellInput); - int selectedRowIndex = getSelectedRowIndex(cellInput); - if (doesUserChooseToPlantFlag(userActionInput)) { - BOARD[selectedRowIndex][selectedColumnIndex] = FLAG_SIGN; - checkIfGameIsOver(); - } else if (doesUserChooseToOpenCell(userActionInput)) { - if (isLandMineCell(selectedRowIndex, selectedColumnIndex)) { - BOARD[selectedRowIndex][selectedColumnIndex] = LAND_MINE_SIGN; - changeGameStatusToLose(); - continue; - } else { - open(selectedRowIndex, selectedColumnIndex); - } - checkIfGameIsOver(); - } else { - System.out.println("잘못된 번호를 선택하셨습니다."); + actOnCell(cellInput, userActionInput); + } + } + + private static void actOnCell(String cellInput, String userActionInput) { + int selectedColumnIndex = getSelectedColIndex(cellInput); + int selectedRowIndex = getSelectedRowIndex(cellInput); + if (doesUserChooseToPlantFlag(userActionInput)) { + BOARD[selectedRowIndex][selectedColumnIndex] = FLAG_SIGN; + checkIfGameIsOver(); + return; + } + if (doesUserChooseToOpenCell(userActionInput)) { + if (isLandMineCell(selectedRowIndex, selectedColumnIndex)) { + BOARD[selectedRowIndex][selectedColumnIndex] = LAND_MINE_SIGN; + changeGameStatusToLose(); + return; } + open(selectedRowIndex, selectedColumnIndex); + checkIfGameIsOver(); + return; } + System.out.println("잘못된 번호를 선택하셨습니다."); } private static void changeGameStatusToLose() { From 5529e4fafea7661873149978523ee6ad21ed12c5 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Fri, 4 Oct 2024 22:15:12 +0900 Subject: [PATCH 06/30] =?UTF-8?q?feat(=EB=85=BC=EB=A6=AC,-=EC=82=AC?= =?UTF-8?q?=EA=B3=A0=EC=9D=98-=ED=9D=90=EB=A6=84):=20[=EB=85=BC=EB=A6=AC,?= =?UTF-8?q?=20=EC=82=AC=EA=B3=A0=EC=9D=98=20=ED=9D=90=EB=A6=84]=20?= =?UTF-8?q?=EC=82=AC=EA=B3=A0=EC=9D=98=20depth=20=EC=A4=84=EC=9D=B4?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/MinesweeperGame.java | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index 515a62afa..ab55d3b32 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -1,11 +1,13 @@ package cleancode.minesweeper.tobe; +import java.util.Arrays; import java.util.Random; import java.util.Scanner; public class MinesweeperGame { + public static final Scanner SCANNER = new Scanner(System.in); public static final int BOARD_ROW_SIZE = 8; public static final int BOARD_COL_SIZE = 10; public static final int LAND_MINE_COUNT = 10; @@ -20,7 +22,6 @@ public class MinesweeperGame { public static void main(String[] args) { showGameStartComments(); - Scanner scanner = new Scanner(System.in); initializeGame(); while (true) { showBoard(); @@ -32,8 +33,8 @@ public static void main(String[] args) { System.out.println("지뢰를 밟았습니다. GAME OVER!"); break; } - String cellInput = getCellInputFromUser(scanner); - String userActionInput = getUserActionInputFromUser(scanner); + String cellInput = getCellInputFromUser(); + String userActionInput = getUserActionInputFromUser(); actOnCell(cellInput, userActionInput); } } @@ -85,14 +86,14 @@ 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() { @@ -111,15 +112,9 @@ private static void checkIfGameIsOver() { } 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) + .flatMap(Arrays::stream) + .noneMatch(cell -> cell.equals(CLOSED_CELL_SIGN)); } private static int convertRowFrom(char cellInputRow) { From d0ce33ada1a13882bb7520aed6ef054103d47d51 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Fri, 4 Oct 2024 23:29:31 +0900 Subject: [PATCH 07/30] =?UTF-8?q?feat(=EB=85=BC=EB=A6=AC,-=EC=82=AC?= =?UTF-8?q?=EA=B3=A0=EC=9D=98-=ED=9D=90=EB=A6=84):=20[=EB=85=BC=EB=A6=AC,?= =?UTF-8?q?=20=EC=82=AC=EA=B3=A0=EC=9D=98=20=ED=9D=90=EB=A6=84]=20?= =?UTF-8?q?=EA=B3=B5=EB=B0=B1=20=EB=9D=BC=EC=9D=B8=EC=9D=84=20=EB=8C=80?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EC=9E=90=EC=84=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cleancode/minesweeper/tobe/MinesweeperGame.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index ab55d3b32..082803a80 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -6,7 +6,6 @@ public class MinesweeperGame { - public static final Scanner SCANNER = new Scanner(System.in); public static final int BOARD_ROW_SIZE = 8; public static final int BOARD_COL_SIZE = 10; @@ -23,8 +22,10 @@ public class MinesweeperGame { public static void main(String[] args) { showGameStartComments(); initializeGame(); + while (true) { showBoard(); + if (doesUserWinTheGame()) { System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); break; @@ -33,6 +34,7 @@ public static void main(String[] args) { System.out.println("지뢰를 밟았습니다. GAME OVER!"); break; } + String cellInput = getCellInputFromUser(); String userActionInput = getUserActionInputFromUser(); actOnCell(cellInput, userActionInput); @@ -42,17 +44,20 @@ public static void main(String[] args) { private static void actOnCell(String cellInput, String userActionInput) { int selectedColumnIndex = getSelectedColIndex(cellInput); int selectedRowIndex = getSelectedRowIndex(cellInput); + if (doesUserChooseToPlantFlag(userActionInput)) { BOARD[selectedRowIndex][selectedColumnIndex] = FLAG_SIGN; checkIfGameIsOver(); return; } + if (doesUserChooseToOpenCell(userActionInput)) { if (isLandMineCell(selectedRowIndex, selectedColumnIndex)) { BOARD[selectedRowIndex][selectedColumnIndex] = LAND_MINE_SIGN; changeGameStatusToLose(); return; } + open(selectedRowIndex, selectedColumnIndex); checkIfGameIsOver(); return; @@ -165,11 +170,13 @@ private static void initializeGame() { BOARD[row][col] = CLOSED_CELL_SIGN; } } + for (int i = 0; i < LAND_MINE_COUNT; i++) { int col = new Random().nextInt(10); int row = new Random().nextInt(8); 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; From a04e022aa29d2b9c9a8f44f53aab9de19131fc86 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Sat, 5 Oct 2024 01:01:22 +0900 Subject: [PATCH 08/30] =?UTF-8?q?feat(=EB=85=BC=EB=A6=AC,-=EC=82=AC?= =?UTF-8?q?=EA=B3=A0=EC=9D=98-=ED=9D=90=EB=A6=84):=20[=EB=85=BC=EB=A6=AC,?= =?UTF-8?q?=20=EC=82=AC=EA=B3=A0=EC=9D=98=20=ED=9D=90=EB=A6=84]=20?= =?UTF-8?q?=EB=B6=80=EC=A0=95=EC=96=B4=EB=A5=BC=20=EB=8C=80=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EC=9E=90=EC=84=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/MinesweeperGame.java | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java index 082803a80..94afbf3a7 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -179,40 +179,45 @@ private static void initializeGame() { 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++; - } - NEARBY_LAND_MINE_COUNTS[row][col] = count; + if (isLandMineCell(row, col)) { + NEARBY_LAND_MINE_COUNTS[row][col] = 0; continue; } - NEARBY_LAND_MINE_COUNTS[row][col] = 0; + int count = countNearByLandMines(row, col); + NEARBY_LAND_MINE_COUNTS[row][col] = count; } } } + private static int countNearByLandMines(int row, int col) { + int count = 0; + 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 showGameStartComments() { System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); System.out.println("지뢰찾기 게임 시작!"); From e5dd92cb8267fca18925c7f4882cc5e0a8feb181 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Sat, 5 Oct 2024 03:00:28 +0900 Subject: [PATCH 09/30] =?UTF-8?q?feat(=EB=85=BC=EB=A6=AC,-=EC=82=AC?= =?UTF-8?q?=EA=B3=A0=EC=9D=98-=ED=9D=90=EB=A6=84):=20[=EB=85=BC=EB=A6=AC,?= =?UTF-8?q?=20=EC=82=AC=EA=B3=A0=EC=9D=98=20=ED=9D=90=EB=A6=84]=20?= =?UTF-8?q?=ED=95=B4=ED=94=BC=20=EC=BC=80=EC=9D=B4=EC=8A=A4=EC=99=80=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/AppException.java | 7 +++ .../minesweeper/tobe/MinesweeperGame.java | 50 +++++++++++-------- 2 files changed, 37 insertions(+), 20 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..d06014edb --- /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 94afbf3a7..346d5916b 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -14,30 +14,35 @@ public class MinesweeperGame { public static final String LAND_MINE_SIGN = "☼"; public static final String CLOSED_CELL_SIGN = "□"; public static final String OPENED_CELL_SIGN = "■"; - private static String[][] BOARD = new String[BOARD_ROW_SIZE][BOARD_COL_SIZE]; - private static Integer[][] NEARBY_LAND_MINE_COUNTS = new Integer[BOARD_ROW_SIZE][BOARD_COL_SIZE]; - private static boolean[][] LAND_MINES = new boolean[BOARD_ROW_SIZE][BOARD_COL_SIZE]; + private static final String[][] BOARD = new String[BOARD_ROW_SIZE][BOARD_COL_SIZE]; + private static final Integer[][] NEARBY_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 int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 public static void main(String[] args) { showGameStartComments(); initializeGame(); - while (true) { - showBoard(); + try { + showBoard(); - if (doesUserWinTheGame()) { - System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); - break; - } - if (doesUserLoseTheGame()) { - System.out.println("지뢰를 밟았습니다. GAME OVER!"); - break; + if (doesUserWinTheGame()) { + System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); + break; + } + if (doesUserLoseTheGame()) { + System.out.println("지뢰를 밟았습니다. GAME OVER!"); + break; + } + String cellInput = getCellInputFromUser(); + String userActionInput = getUserActionInputFromUser(); + actOnCell(cellInput, userActionInput); + } catch (AppException e) { + System.out.println(e.getMessage()); + } catch (Exception e) { + System.out.println("프로그램에 문제가 생겼습니다."); } - - String cellInput = getCellInputFromUser(); - String userActionInput = getUserActionInputFromUser(); - actOnCell(cellInput, userActionInput); } } @@ -62,7 +67,7 @@ private static void actOnCell(String cellInput, String userActionInput) { checkIfGameIsOver(); return; } - System.out.println("잘못된 번호를 선택하셨습니다."); + throw new AppException("잘못된 번호를 선택하셨습니다."); } private static void changeGameStatusToLose() { @@ -119,11 +124,16 @@ private static void checkIfGameIsOver() { private static boolean isAllCellOpened() { return Arrays.stream(BOARD) .flatMap(Arrays::stream) - .noneMatch(cell -> cell.equals(CLOSED_CELL_SIGN)); + .noneMatch(CLOSED_CELL_SIGN::equals); } private static int convertRowFrom(char cellInputRow) { - return Character.getNumericValue(cellInputRow) - 1; + int rowIndex = Character.getNumericValue(cellInputRow) - 1; + if (rowIndex >= BOARD_ROW_SIZE) { + throw new AppException("잘못된 입력입니다."); + } + + return rowIndex; } private static int convertColFrom(char cellInputCol) { @@ -149,7 +159,7 @@ private static int convertColFrom(char cellInputCol) { case 'j': return 9; default: - return -1; + throw new AppException("잘못된 입력입니다."); } } From c4cc31089f912e55ee979d0cd71b859be19f6ee1 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Tue, 15 Oct 2024 03:24:41 +0900 Subject: [PATCH 10/30] =?UTF-8?q?feat(=EA=B0=9D=EC=B2=B4-=EC=A7=80?= =?UTF-8?q?=ED=96=A5-=ED=8C=A8=EB=9F=AC=EB=8B=A4=EC=9E=84):=20[=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=A7=80=ED=96=A5=20=ED=8C=A8=EB=9F=AC=EB=8B=A4?= =?UTF-8?q?=EC=9E=84]=20=EA=B0=9D=EC=B2=B4=20=EC=84=A4=EA=B3=84=ED=95=98?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cleancode/minesweeper/tobe/Cell.java | 85 +++++++++++++++++++ .../minesweeper/tobe/MinesweeperGame.java | 51 ++++++----- 2 files changed, 110 insertions(+), 26 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..381356beb --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/Cell.java @@ -0,0 +1,85 @@ +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 = "■"; + + 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; + } + + // 정적 팩토리 메서드를 좋아하는 이유: 메서드에 이름을 줄 수 있다. + // 정적 팩토리 메서드가 여러개가 된다면 그에 맞는 다른 이름들을 지어줄 수도 있고, 검증과 같은 로직을 추가할 수도 있다. + // 생성자 하나인 객체라도 정적 팩토리 메서드를 만들어서 생성자를 대체해보자. + 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 updateNearbyLandMineCount(int count) { + this.nearbyLandMineCount = count; + } + + 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 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 346d5916b..656c71316 100644 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java @@ -10,14 +10,7 @@ public class MinesweeperGame { public static final int BOARD_ROW_SIZE = 8; public static final int BOARD_COL_SIZE = 10; public static final int LAND_MINE_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 final String[][] BOARD = new String[BOARD_ROW_SIZE][BOARD_COL_SIZE]; - private static final Integer[][] NEARBY_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]; private static int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 public static void main(String[] args) { @@ -51,14 +44,14 @@ private static void actOnCell(String cellInput, String userActionInput) { int selectedRowIndex = getSelectedRowIndex(cellInput); if (doesUserChooseToPlantFlag(userActionInput)) { - BOARD[selectedRowIndex][selectedColumnIndex] = FLAG_SIGN; + BOARD[selectedRowIndex][selectedColumnIndex].flag(); checkIfGameIsOver(); return; } if (doesUserChooseToOpenCell(userActionInput)) { if (isLandMineCell(selectedRowIndex, selectedColumnIndex)) { - BOARD[selectedRowIndex][selectedColumnIndex] = LAND_MINE_SIGN; + BOARD[selectedRowIndex][selectedColumnIndex].open(); changeGameStatusToLose(); return; } @@ -75,7 +68,7 @@ private static void changeGameStatusToLose() { } private static boolean isLandMineCell(int selectedRowIndex, int selectedColumnIndex) { - return LAND_MINES[selectedRowIndex][selectedColumnIndex]; + return BOARD[selectedRowIndex][selectedColumnIndex].isLandMine(); } private static boolean doesUserChooseToOpenCell(String userActionInput) { @@ -115,16 +108,22 @@ private static boolean doesUserWinTheGame() { } private static void checkIfGameIsOver() { - boolean isAllOpened = isAllCellOpened(); - if (isAllOpened) { - gameStatus = 1; + boolean isAllChecked = isAllCellChecked(); + if (isAllChecked) { + changeGameStatusToWin(); } } - private static boolean isAllCellOpened() { + private static void changeGameStatusToWin() { + gameStatus = 1; + } + + // 객체의 캡슐화된 데이터를 외부에서 알고 있다고 생각하지 말자. + // 외부에서는 데이터를 모르니까 짐작해서 물어보는 것이 최선이다. + private static boolean isAllCellChecked() { return Arrays.stream(BOARD) .flatMap(Arrays::stream) - .noneMatch(CLOSED_CELL_SIGN::equals); + .allMatch(Cell::isChecked); } private static int convertRowFrom(char cellInputRow) { @@ -168,7 +167,7 @@ private static void showBoard() { for (int i = 0; i < BOARD_ROW_SIZE; i++) { System.out.printf("%d ", i + 1); for (int j = 0; j < BOARD_COL_SIZE; j++) { - System.out.print(BOARD[i][j] + " "); + System.out.print(BOARD[i][j].getSign() + " "); } System.out.println(); } @@ -177,24 +176,23 @@ 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_MINE_COUNT; i++) { int col = new Random().nextInt(10); int row = new Random().nextInt(8); - 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++) { if (isLandMineCell(row, col)) { - NEARBY_LAND_MINE_COUNTS[row][col] = 0; continue; } int count = countNearByLandMines(row, col); - NEARBY_LAND_MINE_COUNTS[row][col] = count; + BOARD[row][col].updateNearbyLandMineCount(count); } } } @@ -238,18 +236,19 @@ 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)) { + if (BOARD[row][col].isOpened()) { return; } if (isLandMineCell(row, col)) { return; } - if (NEARBY_LAND_MINE_COUNTS[row][col] != 0) { - BOARD[row][col] = String.valueOf(NEARBY_LAND_MINE_COUNTS[row][col]); + + BOARD[row][col].open(); + + if (BOARD[row][col].hasLandMineCount()) { return; - } else { - BOARD[row][col] = OPENED_CELL_SIGN; } + open(row - 1, col - 1); open(row - 1, col); open(row - 1, col + 1); From f40102a88a2f1ac1f065390c76b3bbfc7fc0cd8a Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Tue, 15 Oct 2024 18:42:39 +0900 Subject: [PATCH 11/30] =?UTF-8?q?feat(=EA=B0=9D=EC=B2=B4-=EC=A7=80?= =?UTF-8?q?=ED=96=A5-=ED=8C=A8=EB=9F=AC=EB=8B=A4=EC=9E=84):=20[=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=A7=80=ED=96=A5=20=ED=8C=A8=EB=9F=AC=EB=8B=A4?= =?UTF-8?q?=EC=9E=84]=20SRP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/AppException.java | 7 - .../minesweeper/tobe/GameApplication.java | 12 + .../cleancode/minesweeper/tobe/GameBoard.java | 151 ++++++++++ .../minesweeper/tobe/GameException.java | 7 + .../minesweeper/tobe/Minesweeper.java | 165 +++++++++++ .../minesweeper/tobe/MinesweeperGame.java | 262 ------------------ .../tobe/io/ConsoleInputHandler.java | 11 + .../tobe/io/ConsoleOutputHandler.java | 48 ++++ 8 files changed, 394 insertions(+), 269 deletions(-) delete mode 100644 src/main/java/cleancode/minesweeper/tobe/AppException.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/GameApplication.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/GameBoard.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/GameException.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/Minesweeper.java delete mode 100644 src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.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 d06014edb..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/GameApplication.java b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java new file mode 100644 index 000000000..787a4ee1c --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java @@ -0,0 +1,12 @@ +package cleancode.minesweeper.tobe; + +public class GameApplication { + + // 이 클래스는 딱 프로그램 실행에 진입점만 가지게 된다. + // 이름도 MinesweeperGame 에서 GameApplication 으로 변경한다. -> 이렇게 변경하면 지뢰찾기게임(Minesweeper 뿐만이 아닌 다른 게임도 실행할 수 있게 된다.) + // 게임 실행에 대한 책임과 지뢰찾기 도메인 자체, 지뢰찾기 게임을 담당하는 역할을 분리했다. + public static void main(String[] args) { + Minesweeper minesweeper = new Minesweeper(); + minesweeper.run(); + } +} 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..22c420a47 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -0,0 +1,151 @@ +package cleancode.minesweeper.tobe; + +import java.util.Arrays; +import java.util.Random; + +public class GameBoard { + + private static final int LAND_MINE_COUNT = 10; + + private final Cell[][] board; + + 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 void open(int rowIndex, int colIndex) { + Cell cell = findCell(rowIndex, colIndex); + cell.open(); + } + + public boolean isLandMineCell(int selectedRowIndex, int selectedColumnIndex) { + Cell cell = findCell(selectedRowIndex, selectedColumnIndex); + return cell.isLandMine(); + } + + // 객체의 캡슐화된 데이터를 외부에서 알고 있다고 생각하지 말자. + // 외부에서는 데이터를 모르니까 짐작해서 물어보는 것이 최선이다. + public boolean isAllCellChecked() { + return Arrays.stream(board) + .flatMap(Arrays::stream) + .allMatch(Cell::isChecked); + } + + 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; + } + + open(row, col); + + if (doesCellHaveLandMineCount(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(); + } + + public void initializeGame() { + 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(); + } + } + + for (int i = 0; i < LAND_MINE_COUNT; i++) { + int landMineCol = new Random().nextInt(10); + int landMineRow = new Random().nextInt(8); + Cell landMineCell = findCell(landMineRow, landMineCol); + landMineCell.turnOnLandMine(); + } + + for (int row = 0; row < rowSize; row++) { + for (int col = 0; col < colSize; col++) { + if (isLandMineCell(row, col)) { + continue; + } + int count = countNearByLandMines(row, col); + Cell cell = findCell(row, col); + cell.updateNearbyLandMineCount(count); + } + } + } + + 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 getRowSize() { + return board.length; + } + + public int getColSize() { + return board[0].length; + } + + private int countNearByLandMines(int row, int col) { + int rowSize = getRowSize(); + int colSize = getColSize(); + int count = 0; + + 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; + } +} 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..2b9bae5b8 --- /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 new file mode 100644 index 000000000..f6857c114 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -0,0 +1,165 @@ +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 Scanner SCANNER = new Scanner(System.in); + public static final int BOARD_ROW_SIZE = 8; + public static final int BOARD_COL_SIZE = 10; + public static final int LAND_MINE_COUNT = 10; + + // BOARD 도 하는 일이 너무 많고 중요하기 때문에 Minesweeper 클래스 내부에 상수로 두기에는 너무 책임이 과도하다. + // 이렇게 GameBoard 클래스를 두면 Minesweeper 입장에서는 Cell[][] 이중배열에 대해서는 모른다. + // 객체로 추상화가 되었고, 데이터 구조에 대한 것은 캐슐화가 되었기 때문이다. + private static final GameBoard gameBoard = new GameBoard(BOARD_ROW_SIZE, BOARD_COL_SIZE); + + // 게임이 진행되는 핵심 로직들과 사용자 입출력에 대한 로직 책임을 분리한다. + private final ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(); + private final ConsoleOutputHandler consoleOutputHandler = new ConsoleOutputHandler(); + private int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 + + public void run() { + consoleOutputHandler.showGameStartComments(); + gameBoard.initializeGame(); + while (true) { + try { + consoleOutputHandler.showBoard(gameBoard); + + if (doesUserWinTheGame()) { + consoleOutputHandler.printGameWinningComment(); + break; + } + if (doesUserLoseTheGame()) { + consoleOutputHandler.printGameLosingComment(); + break; + } + String cellInput = getCellInputFromUser(); + String userActionInput = getUserActionInputFromUser(); + actOnCell(cellInput, userActionInput); + } catch (GameException e) { + // print 할 때 AppException 에서 어떤걸 꺼내서 쓸지는 내부에서 알아서 결정할 것이고, + // 예외 상황(exception 에 대한 메시지)에 대한 메시지를 출력하겠다는 이 메서드명을 봤을 때 + // 파라미터는 exception 자체를 넣어주는 것이 더 자연스럽지 않을까 한다. + consoleOutputHandler.printExceptionMessage(e); + } catch (Exception e) { + consoleOutputHandler.printSimpleMessage("프로그램에 문제가 생겼습니다."); + } + } + } + + private void actOnCell(String cellInput, String userActionInput) { + int selectedColumnIndex = getSelectedColIndex(cellInput); + int selectedRowIndex = getSelectedRowIndex(cellInput); + + if (doesUserChooseToPlantFlag(userActionInput)) { + gameBoard.flag(selectedRowIndex, selectedColumnIndex); + checkIfGameIsOver(); + return; + } + + if (doesUserChooseToOpenCell(userActionInput)) { + if (gameBoard.isLandMineCell(selectedRowIndex, selectedColumnIndex)) { + gameBoard.open(selectedRowIndex, selectedColumnIndex); + changeGameStatusToLose(); + return; + } + + gameBoard.openSurroundedCells(selectedRowIndex, selectedColumnIndex); + checkIfGameIsOver(); + return; + } + throw new GameException("잘못된 번호를 선택하셨습니다."); + } + + private void changeGameStatusToLose() { + gameStatus = -1; + } + + private boolean doesUserChooseToOpenCell(String userActionInput) { + return userActionInput.equals("1"); + } + + private boolean doesUserChooseToPlantFlag(String userActionInput) { + return userActionInput.equals("2"); + } + + private int getSelectedRowIndex(String cellInput) { + char cellInputRow = cellInput.charAt(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(); + } + + private String getCellInputFromUser() { + consoleOutputHandler.printCommentForSelectingCell(); + return consoleInputHandler.getUserInput(); + } + + private boolean doesUserLoseTheGame() { + return gameStatus == -1; + } + + private boolean doesUserWinTheGame() { + return gameStatus == 1; + } + + private void checkIfGameIsOver() { + if (gameBoard.isAllCellChecked()) { + changeGameStatusToWin(); + } + } + + private void changeGameStatusToWin() { + gameStatus = 1; + } + + + private int convertRowFrom(char cellInputRow) { + int rowIndex = Character.getNumericValue(cellInputRow) - 1; + if (rowIndex >= BOARD_ROW_SIZE) { + throw new GameException("잘못된 입력입니다."); + } + + return rowIndex; + } + + private int convertColFrom(char cellInputCol) { + 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: + throw new GameException("잘못된 입력입니다."); + } + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java b/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java deleted file mode 100644 index 656c71316..000000000 --- a/src/main/java/cleancode/minesweeper/tobe/MinesweeperGame.java +++ /dev/null @@ -1,262 +0,0 @@ -package cleancode.minesweeper.tobe; - -import java.util.Arrays; -import java.util.Random; -import java.util.Scanner; - -public class MinesweeperGame { - - public static final Scanner SCANNER = new Scanner(System.in); - public static final int BOARD_ROW_SIZE = 8; - public static final int BOARD_COL_SIZE = 10; - public static final int LAND_MINE_COUNT = 10; - private static final Cell[][] BOARD = new Cell[BOARD_ROW_SIZE][BOARD_COL_SIZE]; - private static int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 - - public static void main(String[] args) { - showGameStartComments(); - initializeGame(); - while (true) { - try { - showBoard(); - - if (doesUserWinTheGame()) { - System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); - break; - } - if (doesUserLoseTheGame()) { - System.out.println("지뢰를 밟았습니다. GAME OVER!"); - break; - } - String cellInput = getCellInputFromUser(); - String userActionInput = getUserActionInputFromUser(); - actOnCell(cellInput, userActionInput); - } catch (AppException e) { - System.out.println(e.getMessage()); - } catch (Exception e) { - System.out.println("프로그램에 문제가 생겼습니다."); - } - } - } - - private static void actOnCell(String cellInput, String userActionInput) { - int selectedColumnIndex = getSelectedColIndex(cellInput); - int selectedRowIndex = getSelectedRowIndex(cellInput); - - if (doesUserChooseToPlantFlag(userActionInput)) { - BOARD[selectedRowIndex][selectedColumnIndex].flag(); - checkIfGameIsOver(); - return; - } - - if (doesUserChooseToOpenCell(userActionInput)) { - if (isLandMineCell(selectedRowIndex, selectedColumnIndex)) { - BOARD[selectedRowIndex][selectedColumnIndex].open(); - changeGameStatusToLose(); - return; - } - - open(selectedRowIndex, selectedColumnIndex); - checkIfGameIsOver(); - return; - } - throw new AppException("잘못된 번호를 선택하셨습니다."); - } - - private static void changeGameStatusToLose() { - gameStatus = -1; - } - - private static boolean isLandMineCell(int selectedRowIndex, int selectedColumnIndex) { - return BOARD[selectedRowIndex][selectedColumnIndex].isLandMine(); - } - - 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() { - System.out.println("선택한 셀에 대한 행위를 선택하세요. (1: 오픈, 2: 깃발 꽂기)"); - return SCANNER.nextLine(); - } - - private static String getCellInputFromUser() { - 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 isAllChecked = isAllCellChecked(); - if (isAllChecked) { - changeGameStatusToWin(); - } - } - - private static void changeGameStatusToWin() { - gameStatus = 1; - } - - // 객체의 캡슐화된 데이터를 외부에서 알고 있다고 생각하지 말자. - // 외부에서는 데이터를 모르니까 짐작해서 물어보는 것이 최선이다. - private static boolean isAllCellChecked() { - return Arrays.stream(BOARD) - .flatMap(Arrays::stream) - .allMatch(Cell::isChecked); - } - - private static int convertRowFrom(char cellInputRow) { - int rowIndex = Character.getNumericValue(cellInputRow) - 1; - if (rowIndex >= BOARD_ROW_SIZE) { - throw new AppException("잘못된 입력입니다."); - } - - return rowIndex; - } - - private static int convertColFrom(char cellInputCol) { - 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: - throw new AppException("잘못된 입력입니다."); - } - } - - private static void showBoard() { - System.out.println(" a b c d e f g h i j"); - for (int i = 0; i < BOARD_ROW_SIZE; i++) { - System.out.printf("%d ", i + 1); - for (int j = 0; j < BOARD_COL_SIZE; j++) { - System.out.print(BOARD[i][j].getSign() + " "); - } - 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] = Cell.create(); - } - } - - for (int i = 0; i < LAND_MINE_COUNT; i++) { - int col = new Random().nextInt(10); - int row = new Random().nextInt(8); - BOARD[row][col].turnOnLandMine(); - } - - for (int row = 0; row < BOARD_ROW_SIZE; row++) { - for (int col = 0; col < BOARD_COL_SIZE; col++) { - if (isLandMineCell(row, col)) { - continue; - } - int count = countNearByLandMines(row, col); - BOARD[row][col].updateNearbyLandMineCount(count); - } - } - } - - private static int countNearByLandMines(int row, int col) { - int count = 0; - 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 showGameStartComments() { - System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); - System.out.println("지뢰찾기 게임 시작!"); - System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); - } - - 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].isOpened()) { - return; - } - if (isLandMineCell(row, col)) { - return; - } - - BOARD[row][col].open(); - - if (BOARD[row][col].hasLandMineCount()) { - 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/ConsoleInputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java new file mode 100644 index 000000000..a36a5476c --- /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..7fd39b685 --- /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.GameBoard; +import cleancode.minesweeper.tobe.GameException; +import cleancode.minesweeper.tobe.Cell; + +public class ConsoleOutputHandler { + public void showGameStartComments() { + System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + System.out.println("지뢰찾기 게임 시작!"); + System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + } + + public void showBoard(GameBoard board) { + System.out.println(" a b c d e f g h i j"); + for (int row = 0; row < board.getRowSize(); row++) { + System.out.printf("%d ", row + 1); + for (int col = 0; col < board.getColSize(); col++) { + System.out.print(board.getSign(row, col) + " "); + } + 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 3e7ba5fb3b1a4d143c27e1b7508bd45e4f18414d Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Wed, 16 Oct 2024 03:22:37 +0900 Subject: [PATCH 12/30] =?UTF-8?q?feat(=EA=B0=9D=EC=B2=B4-=EC=A7=80?= =?UTF-8?q?=ED=96=A5-=ED=8C=A8=EB=9F=AC=EB=8B=A4=EC=9E=84):=20[=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=A7=80=ED=96=A5=20=ED=8C=A8=EB=9F=AC=EB=8B=A4?= =?UTF-8?q?=EC=9E=84]=20OCP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/BoardIndexConverter.java | 34 ++++++++++ .../minesweeper/tobe/GameApplication.java | 7 +- .../cleancode/minesweeper/tobe/GameBoard.java | 12 ++-- .../minesweeper/tobe/Minesweeper.java | 64 +++---------------- .../minesweeper/tobe/gamelevel/Advanced.java | 18 ++++++ .../minesweeper/tobe/gamelevel/Beginner.java | 18 ++++++ .../minesweeper/tobe/gamelevel/GameLevel.java | 11 ++++ .../minesweeper/tobe/gamelevel/Middle.java | 18 ++++++ .../tobe/gamelevel/VeryBeginner.java | 20 ++++++ .../tobe/io/ConsoleOutputHandler.java | 18 +++++- 10 files changed, 158 insertions(+), 62 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java 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/BoardIndexConverter.java b/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java new file mode 100644 index 000000000..88f5c72bb --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java @@ -0,0 +1,34 @@ +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 colSize) { + char cellInputCol = cellInput.charAt(0); + return convertColFrom(cellInputCol, colSize); + } + + 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 colSize) { + int colIndex = cellInputCol - BASE_CHAR_FOR_COL; + if (colIndex < 0 || colIndex >= colSize) { + throw new GameException("잘못된 입력입니다."); + } + + return colIndex; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java index 787a4ee1c..6ea9f3d0c 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java @@ -1,12 +1,17 @@ package cleancode.minesweeper.tobe; +import cleancode.minesweeper.tobe.gamelevel.Advanced; +import cleancode.minesweeper.tobe.gamelevel.GameLevel; +import cleancode.minesweeper.tobe.gamelevel.Middle; + public class GameApplication { // 이 클래스는 딱 프로그램 실행에 진입점만 가지게 된다. // 이름도 MinesweeperGame 에서 GameApplication 으로 변경한다. -> 이렇게 변경하면 지뢰찾기게임(Minesweeper 뿐만이 아닌 다른 게임도 실행할 수 있게 된다.) // 게임 실행에 대한 책임과 지뢰찾기 도메인 자체, 지뢰찾기 게임을 담당하는 역할을 분리했다. public static void main(String[] args) { - Minesweeper minesweeper = new Minesweeper(); + GameLevel gameLevel = new Middle(); + 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 22c420a47..ab37af9b3 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -1,16 +1,20 @@ package cleancode.minesweeper.tobe; +import cleancode.minesweeper.tobe.gamelevel.GameLevel; + import java.util.Arrays; import java.util.Random; public class GameBoard { - private static final int LAND_MINE_COUNT = 10; - private final Cell[][] board; + private final int landMineCount; - public GameBoard(int rowSize, int colSize) { + public GameBoard(GameLevel gameLevel) { + int rowSize = gameLevel.getRowSize(); + int colSize = gameLevel.getColSize(); board = new Cell[rowSize][colSize]; + landMineCount = gameLevel.getLandMineCount(); } public void flag(int rowIndex, int colIndex) { @@ -81,7 +85,7 @@ public void initializeGame() { } } - for (int i = 0; i < LAND_MINE_COUNT; i++) { + for (int i = 0; i < landMineCount; i++) { int landMineCol = new Random().nextInt(10); int landMineRow = new Random().nextInt(8); Cell landMineCell = findCell(landMineRow, landMineCol); diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index f6857c114..79c72b1a6 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; @@ -8,21 +9,23 @@ import java.util.Scanner; public class Minesweeper { - public static final Scanner SCANNER = new Scanner(System.in); - public static final int BOARD_ROW_SIZE = 8; - public static final int BOARD_COL_SIZE = 10; - public static final int LAND_MINE_COUNT = 10; // BOARD 도 하는 일이 너무 많고 중요하기 때문에 Minesweeper 클래스 내부에 상수로 두기에는 너무 책임이 과도하다. // 이렇게 GameBoard 클래스를 두면 Minesweeper 입장에서는 Cell[][] 이중배열에 대해서는 모른다. // 객체로 추상화가 되었고, 데이터 구조에 대한 것은 캐슐화가 되었기 때문이다. - private static final GameBoard gameBoard = new GameBoard(BOARD_ROW_SIZE, BOARD_COL_SIZE); + private final GameBoard gameBoard; + // SRP: cellInput 이라는 사용자의 입력을 받아서 rowIndex, colIndex 로 변환하는 역할을 하는 또 하나의 클래스로 볼 수 있지 않을까? + private final BoardIndexConverter boardIndexConverter = new BoardIndexConverter(); // 게임이 진행되는 핵심 로직들과 사용자 입출력에 대한 로직 책임을 분리한다. private final ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(); private final ConsoleOutputHandler consoleOutputHandler = new ConsoleOutputHandler(); private int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 + public Minesweeper(GameLevel gameLevel) { + gameBoard = new GameBoard(gameLevel); + } + public void run() { consoleOutputHandler.showGameStartComments(); gameBoard.initializeGame(); @@ -53,8 +56,8 @@ public void run() { } private void actOnCell(String cellInput, String userActionInput) { - int selectedColumnIndex = getSelectedColIndex(cellInput); - int selectedRowIndex = getSelectedRowIndex(cellInput); + int selectedColumnIndex = boardIndexConverter.getSelectedColIndex(cellInput, gameBoard.getColSize()); + int selectedRowIndex = boardIndexConverter.getSelectedRowIndex(cellInput, gameBoard.getRowSize()); if (doesUserChooseToPlantFlag(userActionInput)) { gameBoard.flag(selectedRowIndex, selectedColumnIndex); @@ -88,16 +91,6 @@ private boolean doesUserChooseToPlantFlag(String userActionInput) { return userActionInput.equals("2"); } - private int getSelectedRowIndex(String cellInput) { - char cellInputRow = cellInput.charAt(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(); @@ -125,41 +118,4 @@ private void checkIfGameIsOver() { private void changeGameStatusToWin() { gameStatus = 1; } - - - private int convertRowFrom(char cellInputRow) { - int rowIndex = Character.getNumericValue(cellInputRow) - 1; - if (rowIndex >= BOARD_ROW_SIZE) { - throw new GameException("잘못된 입력입니다."); - } - - return rowIndex; - } - - private int convertColFrom(char cellInputCol) { - 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: - throw new GameException("잘못된 입력입니다."); - } - } } 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..ed8c30569 --- /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{ + @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..4acb00b42 --- /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{ + @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..82781ea48 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/gamelevel/GameLevel.java @@ -0,0 +1,11 @@ +package cleancode.minesweeper.tobe.gamelevel; + +// 추상화를 정말 바로 보여주는 구조이다. 인터페이스가 갖고 있는 스펙들 즉, 선언된 메서드 선언부들이 이 객체가 어떠한 역할을 갖는지 설명을 해준다. +// 이 GameLevel 인터페이스를 MineSweeper 안에 넣어줄 것이다. +// Minesweeper 객체는 GameLevel 을 받을 것이지만, 인터페이스여서 런타임 시점에 어떤 GameLevel 구현체가 들어오는지는 모른다. 하지만 GameLevel 인터페이스의 스펙은 알고 있다. +// Minesweeper 는 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..7f6892f76 --- /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{ + @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..2cae9dd3b --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/gamelevel/VeryBeginner.java @@ -0,0 +1,20 @@ +package cleancode.minesweeper.tobe.gamelevel; + +public class VeryBeginner implements GameLevel{ + + + @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 7fd39b685..bbc6c4473 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -2,7 +2,9 @@ import cleancode.minesweeper.tobe.GameBoard; import cleancode.minesweeper.tobe.GameException; -import cleancode.minesweeper.tobe.Cell; + +import java.util.List; +import java.util.stream.IntStream; public class ConsoleOutputHandler { public void showGameStartComments() { @@ -12,9 +14,11 @@ public void showGameStartComments() { } 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); for (int col = 0; col < board.getColSize(); col++) { System.out.print(board.getSign(row, col) + " "); } @@ -22,6 +26,14 @@ public void showBoard(GameBoard board) { } } + private String generateColAlphabets(GameBoard board) { + List alphabets = IntStream.range(0, board.getColSize()) + .mapToObj(index -> (char) (index + 'a')) + .map(String::valueOf) + .toList(); + return String.join(" ", alphabets); + } + public void printGameWinningComment() { System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); } From 1c1dff9303ca74e6eeac0233d72851ba628e3002 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Wed, 16 Oct 2024 18:41:50 +0900 Subject: [PATCH 13/30] =?UTF-8?q?feat(=EA=B0=9D=EC=B2=B4-=EC=A7=80?= =?UTF-8?q?=ED=96=A5-=ED=8C=A8=EB=9F=AC=EB=8B=A4=EC=9E=84):=20[=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=A7=80=ED=96=A5=20=ED=8C=A8=EB=9F=AC=EB=8B=A4?= =?UTF-8?q?=EC=9E=84]=20LSP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cleancode/minesweeper/tobe/Cell.java | 85 ------------------- .../cleancode/minesweeper/tobe/GameBoard.java | 41 +++++---- .../cleancode/minesweeper/tobe/cell/Cell.java | 57 +++++++++++++ .../minesweeper/tobe/cell/EmptyCell.java | 27 ++++++ .../minesweeper/tobe/cell/LandMineCell.java | 28 ++++++ .../minesweeper/tobe/cell/NumberCell.java | 32 +++++++ 6 files changed, 167 insertions(+), 103 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 381356beb..000000000 --- a/src/main/java/cleancode/minesweeper/tobe/Cell.java +++ /dev/null @@ -1,85 +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 = "■"; - - 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; - } - - // 정적 팩토리 메서드를 좋아하는 이유: 메서드에 이름을 줄 수 있다. - // 정적 팩토리 메서드가 여러개가 된다면 그에 맞는 다른 이름들을 지어줄 수도 있고, 검증과 같은 로직을 추가할 수도 있다. - // 생성자 하나인 객체라도 정적 팩토리 메서드를 만들어서 생성자를 대체해보자. - 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 updateNearbyLandMineCount(int count) { - this.nearbyLandMineCount = count; - } - - 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 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/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index ab37af9b3..91e7a5e9f 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; @@ -27,19 +31,6 @@ public void open(int rowIndex, int colIndex) { cell.open(); } - public boolean isLandMineCell(int selectedRowIndex, int selectedColumnIndex) { - Cell cell = findCell(selectedRowIndex, selectedColumnIndex); - return cell.isLandMine(); - } - - // 객체의 캡슐화된 데이터를 외부에서 알고 있다고 생각하지 말자. - // 외부에서는 데이터를 모르니까 짐작해서 물어보는 것이 최선이다. - public boolean isAllCellChecked() { - return Arrays.stream(board) - .flatMap(Arrays::stream) - .allMatch(Cell::isChecked); - } - public void openSurroundedCells(int row, int col) { if (row < 0 || row >= getRowSize() || col < 0 || col >= getColSize()) { return; @@ -75,21 +66,33 @@ private boolean isOpenedCell(int row, int col) { return findCell(row, col).isOpened(); } + public boolean isLandMineCell(int selectedRowIndex, int selectedColumnIndex) { + Cell cell = findCell(selectedRowIndex, selectedColumnIndex); + return cell.isLandMine(); + } + // 객체의 캡슐화된 데이터를 외부에서 알고 있다고 생각하지 말자. + // 외부에서는 데이터를 모르니까 짐작해서 물어보는 것이 최선이다. + + public boolean isAllCellChecked() { + return Arrays.stream(board) + .flatMap(Arrays::stream) + .allMatch(Cell::isChecked); + } + public void initializeGame() { 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++) { int landMineCol = new Random().nextInt(10); int landMineRow = new Random().nextInt(8); - Cell landMineCell = findCell(landMineRow, landMineCol); - landMineCell.turnOnLandMine(); + board[landMineRow][landMineCol] = new LandMineCell(); } for (int row = 0; row < rowSize; row++) { @@ -98,8 +101,10 @@ public void initializeGame() { continue; } int count = countNearByLandMines(row, col); - Cell cell = findCell(row, col); - cell.updateNearbyLandMineCount(count); + if (count == 0) { + continue; + } + board[row][col] = new NumberCell(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..5b14d4054 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java @@ -0,0 +1,57 @@ +package cleancode.minesweeper.tobe.cell; + +public abstract class Cell { + + // 하위 클래스에서도 사용할 수 있기 때문에 protected 로 변경 + protected static final String FLAG_SIGN = "⚑"; + protected static final String UNCHECKED_SIGN = "□"; + + + // 하위 클래스에서도 사용할 수 있도록 protected 로 변경 + protected boolean isFlagged; + protected boolean isOpened; + + // Cell 이 가진 속성: 근처 지뢰 갯수, 지뢰 여부 + // Cell 의 상태: 깃발 유무, 열렸다/닫혔다, 사용자가 확인함 + + // 정적 팩토리 메서드를 좋아하는 이유: 메서드에 이름을 줄 수 있다. + // 정적 팩토리 메서드가 여러개가 된다면 그에 맞는 다른 이름들을 지어줄 수도 있고, 검증과 같은 로직을 추가할 수도 있다. + // 생성자 하나인 객체라도 정적 팩토리 메서드를 만들어서 생성자를 대체해보자. + + // 지뢰와 관련된 기능 + // 그런데 구현하고 보니 LandMineCell 은 그자체로 landMine 이라는 의미를 갖고 있는데 turnOnLandMine() 으로 켜주는 것이 이상하다. + // 그리고 해당 기능 때문에 다른 자식 클래스인 EmptyCell, NumberCell 에서는 UnsupportedOperationException 을 던지고 있다. + // 따라서 해당 기능들을 지워야 한다. +// public abstract void turnOnLandMine(); + + // 이것도 특정 셀에서만 유효하다. + // 이것도 위와 마찬가지로 NumberCell 에서는 그 자체로 count 를 필드로 갖고 있어야 하지 메서드로 조정할 것이 아니다. + // 메서드로 조정하다보니 다른 자식 클래스에서 UnsupportedOperationException 을 던지고 있다. + // 따라서 해당 기능들을 지워야 한다. +// public abstract void updateNearbyLandMineCount(int count); + + + // isOpened, isFlagged 는 Cell 의 공통 기능이므로 그대로 둔다. + public void flag() { + this.isFlagged = true; + } + + public void open() { + this.isOpened = true; + } + + public boolean isChecked() { + return isFlagged || isOpened; + } + + // 이것도 특정 셀에서만 유효하다. + public abstract boolean isLandMine(); + + public boolean isOpened() { + return isOpened; + } + + public abstract boolean hasLandMineCount(); + + public abstract String getSign(); +} 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..ec9011110 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java @@ -0,0 +1,27 @@ +package cleancode.minesweeper.tobe.cell; + +public class EmptyCell extends Cell { + + private 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..a369ab7d6 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java @@ -0,0 +1,28 @@ +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..30f2a5267 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java @@ -0,0 +1,32 @@ +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 2019ceb25b74bc77ff4c0c3190c80ba202d83d75 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Wed, 16 Oct 2024 21:31:02 +0900 Subject: [PATCH 14/30] =?UTF-8?q?feat(=EA=B0=9D=EC=B2=B4-=EC=A7=80?= =?UTF-8?q?=ED=96=A5-=ED=8C=A8=EB=9F=AC=EB=8B=A4=EC=9E=84):=20[=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=A7=80=ED=96=A5=20=ED=8C=A8=EB=9F=AC=EB=8B=A4?= =?UTF-8?q?=EC=9E=84]=20ISP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/GameApplication.java | 1 + .../cleancode/minesweeper/tobe/Minesweeper.java | 15 +++++++++------ .../minesweeper/tobe/game/GameInitializable.java | 5 +++++ .../minesweeper/tobe/game/GameRunnable.java | 5 +++++ 4 files changed, 20 insertions(+), 6 deletions(-) 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/GameApplication.java b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java index 6ea9f3d0c..4cdede2de 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java @@ -12,6 +12,7 @@ public class GameApplication { public static void main(String[] args) { GameLevel gameLevel = new Middle(); Minesweeper minesweeper = new Minesweeper(gameLevel); + 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 79c72b1a6..248a16fea 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -1,14 +1,12 @@ 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; -import java.util.Arrays; -import java.util.Random; -import java.util.Scanner; - -public class Minesweeper { +public class Minesweeper implements GameInitializable, GameRunnable { // BOARD 도 하는 일이 너무 많고 중요하기 때문에 Minesweeper 클래스 내부에 상수로 두기에는 너무 책임이 과도하다. // 이렇게 GameBoard 클래스를 두면 Minesweeper 입장에서는 Cell[][] 이중배열에 대해서는 모른다. @@ -26,9 +24,14 @@ public Minesweeper(GameLevel gameLevel) { gameBoard = new GameBoard(gameLevel); } + @Override + public void initialize() { + gameBoard.initializeGame(); + } + + @Override public void run() { consoleOutputHandler.showGameStartComments(); - gameBoard.initializeGame(); while (true) { try { consoleOutputHandler.showBoard(gameBoard); 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..4acf16e46 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/game/GameInitializable.java @@ -0,0 +1,5 @@ +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..1d6c157bc --- /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 173ead6804d9681258dcf60c9813e206adb67853 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Thu, 17 Oct 2024 14:49:12 +0900 Subject: [PATCH 15/30] =?UTF-8?q?feat(=EA=B0=9D=EC=B2=B4-=EC=A7=80?= =?UTF-8?q?=ED=96=A5-=ED=8C=A8=EB=9F=AC=EB=8B=A4=EC=9E=84):=20[=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=A7=80=ED=96=A5=20=ED=8C=A8=EB=9F=AC=EB=8B=A4?= =?UTF-8?q?=EC=9E=84]=20DIP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/GameApplication.java | 8 +++- .../minesweeper/tobe/Minesweeper.java | 34 ++++++++------- .../tobe/io/ConsoleInputHandler.java | 3 +- .../tobe/io/ConsoleOutputHandler.java | 23 +++++++---- .../minesweeper/tobe/io/InputHandler.java | 5 +++ .../minesweeper/tobe/io/OutputHandler.java | 41 +++++++++++++++++++ 6 files changed, 90 insertions(+), 24 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 4cdede2de..9ad5102db 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java @@ -3,6 +3,9 @@ import cleancode.minesweeper.tobe.gamelevel.Advanced; import cleancode.minesweeper.tobe.gamelevel.GameLevel; import cleancode.minesweeper.tobe.gamelevel.Middle; +import cleancode.minesweeper.tobe.io.ConsoleInputHandler; +import cleancode.minesweeper.tobe.io.ConsoleOutputHandler; +import cleancode.minesweeper.tobe.io.InputHandler; public class GameApplication { @@ -11,7 +14,10 @@ public class GameApplication { // 게임 실행에 대한 책임과 지뢰찾기 도메인 자체, 지뢰찾기 게임을 담당하는 역할을 분리했다. public static void main(String[] args) { GameLevel gameLevel = new Middle(); - Minesweeper minesweeper = new Minesweeper(gameLevel); + InputHandler inputHandler = new ConsoleInputHandler(); + ConsoleOutputHandler outputHandler = new ConsoleOutputHandler(); + + 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 248a16fea..2ae93d003 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -3,8 +3,8 @@ 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; public class Minesweeper implements GameInitializable, GameRunnable { @@ -16,12 +16,16 @@ public class Minesweeper implements GameInitializable, GameRunnable { private final BoardIndexConverter boardIndexConverter = new BoardIndexConverter(); // 게임이 진행되는 핵심 로직들과 사용자 입출력에 대한 로직 책임을 분리한다. - private final ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(); - private final ConsoleOutputHandler consoleOutputHandler = new ConsoleOutputHandler(); + // DIP: InputHandler, OutputHandler 는 이제 Console 에 관한 것은 모른다. 인터페이스만 의존하고 있다. + // 구현체가 변경되어도 Minesweeper 클래스는 영향을 받지 않는다. + 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 @@ -31,17 +35,17 @@ public void initialize() { @Override public void run() { - consoleOutputHandler.showGameStartComments(); + outputHandler.showGameStartComments(); while (true) { try { - consoleOutputHandler.showBoard(gameBoard); + outputHandler.showBoard(gameBoard); if (doesUserWinTheGame()) { - consoleOutputHandler.printGameWinningComment(); + outputHandler.showPrintGameWinningComment(); break; } if (doesUserLoseTheGame()) { - consoleOutputHandler.printGameLosingComment(); + outputHandler.showGameLosingComment(); break; } String cellInput = getCellInputFromUser(); @@ -51,9 +55,9 @@ public void run() { // print 할 때 AppException 에서 어떤걸 꺼내서 쓸지는 내부에서 알아서 결정할 것이고, // 예외 상황(exception 에 대한 메시지)에 대한 메시지를 출력하겠다는 이 메서드명을 봤을 때 // 파라미터는 exception 자체를 넣어주는 것이 더 자연스럽지 않을까 한다. - consoleOutputHandler.printExceptionMessage(e); + outputHandler.showExceptionMessage(e); } catch (Exception e) { - consoleOutputHandler.printSimpleMessage("프로그램에 문제가 생겼습니다."); + outputHandler.showSimpleMessage("프로그램에 문제가 생겼습니다."); } } } @@ -95,13 +99,13 @@ private boolean doesUserChooseToPlantFlag(String userActionInput) { } private String getUserActionInputFromUser() { - consoleOutputHandler.printCommentForUserAction(); - return consoleInputHandler.getUserInput(); + outputHandler.showCommentForUserAction(); + return inputHandler.getUserInput(); } private String getCellInputFromUser() { - consoleOutputHandler.printCommentForSelectingCell(); - return consoleInputHandler.getUserInput(); + outputHandler.showCommentForSelectingCell(); + 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 a36a5476c..8dc1a0366 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 bbc6c4473..72f0d3af3 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -6,13 +6,16 @@ import java.util.List; import java.util.stream.IntStream; -public class ConsoleOutputHandler { +public class ConsoleOutputHandler implements OutputHandler { + + @Override public void showGameStartComments() { System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); System.out.println("지뢰찾기 게임 시작!"); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); } + @Override public void showBoard(GameBoard board) { String alphabets = generateColAlphabets(board); @@ -34,27 +37,33 @@ private String generateColAlphabets(GameBoard board) { return String.join(" ", alphabets); } - public void printGameWinningComment() { + @Override + public void showPrintGameWinningComment() { System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); } - public void printGameLosingComment() { + @Override + public void showGameLosingComment() { System.out.println("지뢰를 밟았습니다. GAME OVER!"); } - public void printCommentForSelectingCell() { + @Override + public void showCommentForSelectingCell() { System.out.println("선택할 좌표를 입력하세요. (예: a1)"); } - public void printCommentForUserAction() { + @Override + public void showCommentForUserAction() { System.out.println("선택한 셀에 대한 행위를 선택하세요. (1: 오픈, 2: 깃발 꽂기)"); } - public void printExceptionMessage(GameException e) { + @Override + public void showExceptionMessage(GameException e) { System.out.println(e.getMessage()); } - public void printSimpleMessage(String message) { + @Override + public void showSimpleMessage(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..639402524 --- /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..7552cadd2 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java @@ -0,0 +1,41 @@ +package cleancode.minesweeper.tobe.io; + +import cleancode.minesweeper.tobe.GameBoard; +import cleancode.minesweeper.tobe.GameException; + +public interface OutputHandler { + + // show, print 중에 show 는 추상적인 느낌이고, print 는 구체적인 느낌이 강하다. + // print 는 console 에 print 한다는 느낌이 강하다. + // 높은 추상화 레벨인 OutputHandler 입장에서는 print 보다는 show 가 더 낫겠다. + + void showGameStartComments(); + + void showBoard(GameBoard board); + + + void showPrintGameWinningComment(); + + void showGameLosingComment(); + + void showCommentForSelectingCell(); + + void showCommentForUserAction(); + + void showExceptionMessage(GameException e); + + void showSimpleMessage(String message); + + +// void printGameWinningComment(); + +// void printGameLosingComment(); + +// void printCommentForSelectingCell(); + +// void printCommentForUserAction(); + +// void printExceptionMessage(GameException e); +// +// void printSimpleMessage(String message); +} From 579ffd5626e690152921584ae47f8ae0b9b0762f Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Wed, 12 Mar 2025 18:22:24 +0900 Subject: [PATCH 16/30] =?UTF-8?q?feat(=EA=B0=9D=EC=B2=B4-=EC=A7=80?= =?UTF-8?q?=ED=96=A5-=EC=A0=81=EC=9A=A9):=20=EC=83=81=EC=86=8D=20=EB=8C=80?= =?UTF-8?q?=EC=8B=A0=20=EC=A1=B0=ED=95=A9,=20Value=20Object?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 0 -> 6148 bytes .../minesweeper/tobe/BoardIndexConverter.java | 16 +-- .../cleancode/minesweeper/tobe/GameBoard.java | 115 +++++++++--------- .../minesweeper/tobe/Minesweeper.java | 28 +++-- .../cleancode/minesweeper/tobe/cell/Cell.java | 56 ++------- .../minesweeper/tobe/cell/CellState.java | 37 ++++++ .../minesweeper/tobe/cell/EmptyCell.java | 28 ++++- .../minesweeper/tobe/cell/LandMineCell.java | 27 +++- .../minesweeper/tobe/cell/NumberCell.java | 28 ++++- .../tobe/io/ConsoleInputHandler.java | 13 ++ .../tobe/io/ConsoleOutputHandler.java | 4 +- .../minesweeper/tobe/io/InputHandler.java | 4 + .../tobe/positoion/CellPosition.java | 72 +++++++++++ .../tobe/positoion/RelativePosition.java | 50 ++++++++ 14 files changed, 342 insertions(+), 136 deletions(-) create mode 100644 .DS_Store create mode 100644 src/main/java/cleancode/minesweeper/tobe/cell/CellState.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/positoion/CellPosition.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/positoion/RelativePosition.java diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..0a768b9b71ee7a28d279f5958670432cdad6353c GIT binary patch literal 6148 zcmeHK%}T>S5Z<-XrW7Fug&r5Y7Oln##Y>3w1&ruHr6#1*V9b^_HHT8jSzpK}@p+ut z-H63{6|pn0`_1oe_JiyXV~qQ=_>eJ&F=j(U`_07u zI^efkY@dx-!t$@*AC1#wns++ye4}PFg|Iguoq>F$eggRp27Z~^eLJmph literal 0 HcmV?d00001 diff --git a/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java b/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java index 88f5c72bb..b7144491e 100644 --- a/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java +++ b/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java @@ -4,28 +4,28 @@ 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 colSize) { + public int getSelectedColIndex(String cellInput) { char cellInputCol = cellInput.charAt(0); - return convertColFrom(cellInputCol, colSize); + 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 colSize) { + private int convertColFrom(char cellInputCol) { int colIndex = cellInputCol - BASE_CHAR_FOR_COL; - if (colIndex < 0 || colIndex >= colSize) { + if (colIndex < 0) { throw new GameException("잘못된 입력입니다."); } diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index 91e7a5e9f..2b303044c 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -5,9 +5,13 @@ import cleancode.minesweeper.tobe.cell.LandMineCell; import cleancode.minesweeper.tobe.cell.NumberCell; import cleancode.minesweeper.tobe.gamelevel.GameLevel; +import cleancode.minesweeper.tobe.positoion.CellPosition; +import cleancode.minesweeper.tobe.positoion.RelativePosition; import java.util.Arrays; +import java.util.List; import java.util.Random; +import java.util.stream.Stream; public class GameBoard { @@ -21,53 +25,46 @@ public GameBoard(GameLevel gameLevel) { landMineCount = gameLevel.getLandMineCount(); } - public void flag(int rowIndex, int colIndex) { - Cell cell = findCell(rowIndex, colIndex); + public void flagAt(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); cell.flag(); } - public void open(int rowIndex, int colIndex) { - Cell 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 (isOpenedCell(cellPosition)) { return; } - if (isOpenedCell(row, col)) { - return; - } - if (isLandMineCell(row, col)) { + if (isLandMineCellAt(cellPosition)) { return; } - open(row, col); + openAt(cellPosition); - if (doesCellHaveLandMineCount(row, col)) { + if (doesCellHaveLandMineCount(cellPosition)) { 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); + List surroundedPositions = calculateSurroundedPosition(cellPosition, getRowSize(), getColSize()); + surroundedPositions.forEach(this::openSurroundedCells); } - 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(); } - public boolean isLandMineCell(int selectedRowIndex, int selectedColumnIndex) { - Cell cell = findCell(selectedRowIndex, selectedColumnIndex); + public boolean isLandMineCellAt(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); return cell.isLandMine(); } // 객체의 캡슐화된 데이터를 외부에서 알고 있다고 생각하지 말자. @@ -79,6 +76,15 @@ public boolean isAllCellChecked() { .allMatch(Cell::isChecked); } + + public boolean isInValidCellPosition(CellPosition cellPosition) { + int rowSize = getRowSize(); + int colSize = getRowSize(); + + return cellPosition.isRowIndexMoreThanOrEqual(rowSize) + || cellPosition.isColIndexMoreThanOrEqual(colSize); + } + public void initializeGame() { int rowSize = getRowSize(); int colSize = getColSize(); @@ -97,10 +103,12 @@ public void initializeGame() { for (int row = 0; row < rowSize; row++) { for (int col = 0; col < colSize; col++) { - if (isLandMineCell(row, col)) { + CellPosition cellPosition = CellPosition.of(row, col); + + if (isLandMineCellAt(cellPosition)) { continue; } - int count = countNearByLandMines(row, col); + int count = countNearByLandMines(cellPosition); if (count == 0) { continue; } @@ -109,13 +117,13 @@ public void initializeGame() { } } - public String getSign(int rowIndex, int colIndex) { - Cell cell = findCell(rowIndex, colIndex); + public String getSign(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); return cell.getSign(); } - private Cell findCell(int rowIndex, int colIndex) { - return board[rowIndex][colIndex]; + private Cell findCell(CellPosition cellPosition) { + return board[cellPosition.getRowIndex()][cellPosition.getColIndex()]; } public int getRowSize() { @@ -126,35 +134,24 @@ public int getColSize() { return board[0].length; } - private int countNearByLandMines(int row, int col) { + private int countNearByLandMines(CellPosition cellPosition) { int rowSize = getRowSize(); int colSize = getColSize(); - int count = 0; - 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 = calculateSurroundedPosition(cellPosition, rowSize, colSize).stream() + .filter(this::isLandMineCellAt) + .count(); + + return (int) count; + } + + private List calculateSurroundedPosition(CellPosition cellPosition, int rowSize, int colSize) { + return RelativePosition.SURROUNDED_POSITIONS.stream() + .filter(cellPosition::canCalculatePositionBy) + .map(cellPosition::calculatePositionBy) + .filter(position -> position.isRowIndexLessThan(rowSize)) + .filter(position -> position.isColIndexLessThan(colSize)) + .toList(); } + } diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index 2ae93d003..86e55c06a 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.positoion.CellPosition; public class Minesweeper implements GameInitializable, GameRunnable { @@ -13,7 +14,6 @@ public class Minesweeper implements GameInitializable, GameRunnable { // 객체로 추상화가 되었고, 데이터 구조에 대한 것은 캐슐화가 되었기 때문이다. private final GameBoard gameBoard; // SRP: cellInput 이라는 사용자의 입력을 받아서 rowIndex, colIndex 로 변환하는 역할을 하는 또 하나의 클래스로 볼 수 있지 않을까? - private final BoardIndexConverter boardIndexConverter = new BoardIndexConverter(); // 게임이 진행되는 핵심 로직들과 사용자 입출력에 대한 로직 책임을 분리한다. // DIP: InputHandler, OutputHandler 는 이제 Console 에 관한 것은 모른다. 인터페이스만 의존하고 있다. @@ -48,9 +48,9 @@ public void run() { outputHandler.showGameLosingComment(); break; } - String cellInput = getCellInputFromUser(); + CellPosition cellPosition = getCellInputFromUser(); String userActionInput = getUserActionInputFromUser(); - actOnCell(cellInput, userActionInput); + actOnCell(cellPosition, userActionInput); } catch (GameException e) { // print 할 때 AppException 에서 어떤걸 꺼내서 쓸지는 내부에서 알아서 결정할 것이고, // 예외 상황(exception 에 대한 메시지)에 대한 메시지를 출력하겠다는 이 메서드명을 봤을 때 @@ -62,24 +62,21 @@ public void run() { } } - private void actOnCell(String cellInput, String userActionInput) { - int selectedColumnIndex = 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, selectedColumnIndex); + gameBoard.flagAt(cellPosition); checkIfGameIsOver(); return; } if (doesUserChooseToOpenCell(userActionInput)) { - if (gameBoard.isLandMineCell(selectedRowIndex, selectedColumnIndex)) { - gameBoard.open(selectedRowIndex, selectedColumnIndex); + if (gameBoard.isLandMineCellAt(cellPosition)) { + gameBoard.openAt(cellPosition); changeGameStatusToLose(); return; } - gameBoard.openSurroundedCells(selectedRowIndex, selectedColumnIndex); + gameBoard.openSurroundedCells(cellPosition); checkIfGameIsOver(); return; } @@ -103,9 +100,14 @@ private String getUserActionInputFromUser() { return inputHandler.getUserInput(); } - private String getCellInputFromUser() { + private CellPosition getCellInputFromUser() { outputHandler.showCommentForSelectingCell(); - return inputHandler.getUserInput(); + CellPosition cellPosition = inputHandler.getCellPositionFromUser(); + if (gameBoard.isInValidCellPosition(cellPosition)) { + throw new GameException("잘못된 좌표를 선택하셨습니다."); + } + + return cellPosition; } private boolean doesUserLoseTheGame() { diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java b/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java index 5b14d4054..7b8d9bcbb 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java @@ -1,57 +1,21 @@ package cleancode.minesweeper.tobe.cell; -public abstract class Cell { +public interface Cell { // 하위 클래스에서도 사용할 수 있기 때문에 protected 로 변경 - protected static final String FLAG_SIGN = "⚑"; - protected static final String UNCHECKED_SIGN = "□"; - - - // 하위 클래스에서도 사용할 수 있도록 protected 로 변경 - protected boolean isFlagged; - protected boolean isOpened; - - // Cell 이 가진 속성: 근처 지뢰 갯수, 지뢰 여부 - // Cell 의 상태: 깃발 유무, 열렸다/닫혔다, 사용자가 확인함 - - // 정적 팩토리 메서드를 좋아하는 이유: 메서드에 이름을 줄 수 있다. - // 정적 팩토리 메서드가 여러개가 된다면 그에 맞는 다른 이름들을 지어줄 수도 있고, 검증과 같은 로직을 추가할 수도 있다. - // 생성자 하나인 객체라도 정적 팩토리 메서드를 만들어서 생성자를 대체해보자. - - // 지뢰와 관련된 기능 - // 그런데 구현하고 보니 LandMineCell 은 그자체로 landMine 이라는 의미를 갖고 있는데 turnOnLandMine() 으로 켜주는 것이 이상하다. - // 그리고 해당 기능 때문에 다른 자식 클래스인 EmptyCell, NumberCell 에서는 UnsupportedOperationException 을 던지고 있다. - // 따라서 해당 기능들을 지워야 한다. -// public abstract void turnOnLandMine(); - - // 이것도 특정 셀에서만 유효하다. - // 이것도 위와 마찬가지로 NumberCell 에서는 그 자체로 count 를 필드로 갖고 있어야 하지 메서드로 조정할 것이 아니다. - // 메서드로 조정하다보니 다른 자식 클래스에서 UnsupportedOperationException 을 던지고 있다. - // 따라서 해당 기능들을 지워야 한다. -// public abstract void updateNearbyLandMineCount(int count); + String FLAG_SIGN = "⚑"; + String UNCHECKED_SIGN = "□"; + boolean isLandMine(); + boolean hasLandMineCount(); + String getSign(); // isOpened, isFlagged 는 Cell 의 공통 기능이므로 그대로 둔다. - public void flag() { - this.isFlagged = true; - } - - public void open() { - this.isOpened = true; - } - - public boolean isChecked() { - return isFlagged || isOpened; - } - - // 이것도 특정 셀에서만 유효하다. - public abstract boolean isLandMine(); + void flag(); - public boolean isOpened() { - return isOpened; - } + void open(); - public abstract boolean hasLandMineCount(); + boolean isChecked(); - public abstract String getSign(); + 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..98de7fe51 --- /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); + } + + // isOpened, isFlagged 는 Cell 의 공통 기능이므로 그대로 둔다. + 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 ec9011110..0021c03e1 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java @@ -1,9 +1,11 @@ package cleancode.minesweeper.tobe.cell; -public class EmptyCell extends Cell { +public class EmptyCell implements Cell { private static final String EMPTY_SIGN = "■"; + private final CellState cellState = CellState.initialize(); + @Override public boolean isLandMine() { return false; @@ -16,12 +18,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 a369ab7d6..e79b94774 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java @@ -1,8 +1,9 @@ package cleancode.minesweeper.tobe.cell; -public class LandMineCell extends Cell { +public class LandMineCell implements Cell { private static final String LAND_MINE_SIGN = "☼"; + private final CellState cellState = CellState.initialize(); @Override public boolean isLandMine() { @@ -16,13 +17,33 @@ 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 30f2a5267..4c2431d1f 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java @@ -1,8 +1,10 @@ package cleancode.minesweeper.tobe.cell; -public class NumberCell extends Cell { +public class NumberCell implements Cell { private final int nearbyLandMineCount; + private final CellState cellState = CellState.initialize(); + public NumberCell(int nearbyLandMineCount) { this.nearbyLandMineCount = nearbyLandMineCount; @@ -20,13 +22,33 @@ 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(); + } } diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java index 8dc1a0366..23e5b4455 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java @@ -1,12 +1,25 @@ package cleancode.minesweeper.tobe.io; +import cleancode.minesweeper.tobe.BoardIndexConverter; +import cleancode.minesweeper.tobe.positoion.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 rowIndex = boardIndexConverter.getSelectedColIndex(userInput); + int colIndex = boardIndexConverter.getSelectedColIndex(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 72f0d3af3..157870c80 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.positoion.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); for (int col = 0; col < board.getColSize(); col++) { - System.out.print(board.getSign(row, col) + " "); + CellPosition cellPosition = CellPosition.of(row, col); + System.out.print(board.getSign(cellPosition) + " "); } 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 639402524..cc464f241 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java @@ -1,5 +1,9 @@ package cleancode.minesweeper.tobe.io; +import cleancode.minesweeper.tobe.positoion.CellPosition; + public interface InputHandler { String getUserInput(); + + CellPosition getCellPositionFromUser(); } diff --git a/src/main/java/cleancode/minesweeper/tobe/positoion/CellPosition.java b/src/main/java/cleancode/minesweeper/tobe/positoion/CellPosition.java new file mode 100644 index 000000000..94e0bee25 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/positoion/CellPosition.java @@ -0,0 +1,72 @@ +package cleancode.minesweeper.tobe.positoion; + +import java.util.Objects; + +public class CellPosition { + private final int rowIndex; + private final int colIndex; + + private CellPosition(int rowIndex, int colIndex) { + if (rowIndex < 0 || colIndex < 0) { + throw new IllegalArgumentException("올바르지 않은 좌표입니다."); + } + this.rowIndex = rowIndex; + this.colIndex = colIndex; + } + + public static CellPosition of(int rowIndex, int colIndex) { + return new CellPosition(rowIndex, colIndex); + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + CellPosition that = (CellPosition) object; + 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 this.rowIndex; + } + + public int getColIndex() { + return this.colIndex; + } + + public boolean canCalculatePositionBy(RelativePosition relativePosition) { + return this.getRowIndex() + relativePosition.getDeltaRow() >= 0 + && this.getColIndex() + 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("움직일 수 없는 좌표입니다."); + } + + public boolean isRowIndexLessThan(int rowIndex) { + return this.rowIndex < rowIndex; + } + + public boolean isColIndexLessThan(int colIndex) { + return this.colIndex < colIndex; + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/positoion/RelativePosition.java b/src/main/java/cleancode/minesweeper/tobe/positoion/RelativePosition.java new file mode 100644 index 000000000..8989080a9 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/positoion/RelativePosition.java @@ -0,0 +1,50 @@ +package cleancode.minesweeper.tobe.positoion; + +import java.util.List; +import java.util.Objects; + +public class RelativePosition { + public static final List SURROUNDED_POSITIONS = 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 object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + RelativePosition that = (RelativePosition) object; + return deltaRow == that.deltaRow && deltaCol == that.deltaCol; + } + + @Override + public int hashCode() { + return Objects.hash(deltaRow, deltaCol); + } + + public int getDeltaRow() { + return this.deltaRow; + } + + public int getDeltaCol() { + return this.deltaCol; + } +} From 73ca430725d0805b98813e1bd649aeef3400e3c3 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Wed, 12 Mar 2025 21:49:48 +0900 Subject: [PATCH 17/30] =?UTF-8?q?feat(=EA=B0=9D=EC=B2=B4-=EC=A7=80?= =?UTF-8?q?=ED=96=A5-=EC=A0=81=EC=9A=A9=ED=95=98=EA=B8=B0):=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=89=20=EC=BB=AC=EB=A0=89=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleancode/minesweeper/tobe/GameBoard.java | 79 ++++++++++--------- .../minesweeper/tobe/Minesweeper.java | 10 +-- .../minesweeper/tobe/cell/Cells.java | 28 +++++++ .../tobe/io/ConsoleOutputHandler.java | 2 +- .../minesweeper/tobe/io/OutputHandler.java | 2 +- .../tobe/positoion/CellPosition.java | 19 ++--- .../tobe/positoion/CellPositions.java | 57 +++++++++++++ .../tobe/positoion/RelativePosition.java | 16 ++-- 8 files changed, 151 insertions(+), 62 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/cell/Cells.java create mode 100644 src/main/java/cleancode/minesweeper/tobe/positoion/CellPositions.java diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index 2b303044c..a021cfcdc 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -1,18 +1,16 @@ package cleancode.minesweeper.tobe; import cleancode.minesweeper.tobe.cell.Cell; +import cleancode.minesweeper.tobe.cell.Cells; 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 cleancode.minesweeper.tobe.positoion.CellPosition; +import cleancode.minesweeper.tobe.positoion.CellPositions; import cleancode.minesweeper.tobe.positoion.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; @@ -22,6 +20,7 @@ public GameBoard(GameLevel gameLevel) { int rowSize = gameLevel.getRowSize(); int colSize = gameLevel.getColSize(); board = new Cell[rowSize][colSize]; + landMineCount = gameLevel.getLandMineCount(); } @@ -49,7 +48,7 @@ public void openSurroundedCells(CellPosition cellPosition) { return; } - List surroundedPositions = calculateSurroundedPosition(cellPosition, getRowSize(), getColSize()); + List surroundedPositions = calculateSurroundedPositions(cellPosition, getRowSize(), getColSize()); surroundedPositions.forEach(this::openSurroundedCells); } @@ -67,56 +66,58 @@ public boolean isLandMineCellAt(CellPosition cellPosition) { Cell cell = findCell(cellPosition); return cell.isLandMine(); } - // 객체의 캡슐화된 데이터를 외부에서 알고 있다고 생각하지 말자. - // 외부에서는 데이터를 모르니까 짐작해서 물어보는 것이 최선이다. public boolean isAllCellChecked() { - return Arrays.stream(board) - .flatMap(Arrays::stream) - .allMatch(Cell::isChecked); + Cells cells = Cells.from(board); + return cells.isAllChecked(); } - - public boolean isInValidCellPosition(CellPosition cellPosition) { + public boolean isInvalidCellPosition(CellPosition cellPosition) { int rowSize = getRowSize(); - int colSize = getRowSize(); + int colSize = getColSize(); return cellPosition.isRowIndexMoreThanOrEqual(rowSize) || cellPosition.isColIndexMoreThanOrEqual(colSize); } 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); + 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++) { - int landMineCol = new Random().nextInt(10); - int landMineRow = new Random().nextInt(8); - board[landMineRow][landMineCol] = new LandMineCell(); + 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)) { - continue; - } - int count = countNearByLandMines(cellPosition); - if (count == 0) { - continue; - } - board[row][col] = new NumberCell(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; + } + public String getSign(CellPosition cellPosition) { Cell cell = findCell(cellPosition); return cell.getSign(); @@ -134,18 +135,18 @@ public int getColSize() { return board[0].length; } - private int countNearByLandMines(CellPosition cellPosition) { + private int countNearbyLandMines(CellPosition cellPosition) { int rowSize = getRowSize(); int colSize = getColSize(); - long count = calculateSurroundedPosition(cellPosition, rowSize, colSize).stream() + long count = calculateSurroundedPositions(cellPosition, rowSize, colSize).stream() .filter(this::isLandMineCellAt) .count(); - + return (int) count; } - private List calculateSurroundedPosition(CellPosition cellPosition, int rowSize, int colSize) { + private List calculateSurroundedPositions(CellPosition cellPosition, int rowSize, int colSize) { return RelativePosition.SURROUNDED_POSITIONS.stream() .filter(cellPosition::canCalculatePositionBy) .map(cellPosition::calculatePositionBy) diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index 86e55c06a..f649a6e18 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -36,25 +36,24 @@ public void initialize() { @Override public void run() { outputHandler.showGameStartComments(); + while (true) { try { outputHandler.showBoard(gameBoard); if (doesUserWinTheGame()) { - outputHandler.showPrintGameWinningComment(); + outputHandler.showGameWinningComment(); break; } if (doesUserLoseTheGame()) { outputHandler.showGameLosingComment(); break; } + CellPosition cellPosition = getCellInputFromUser(); String userActionInput = getUserActionInputFromUser(); actOnCell(cellPosition, userActionInput); } catch (GameException e) { - // print 할 때 AppException 에서 어떤걸 꺼내서 쓸지는 내부에서 알아서 결정할 것이고, - // 예외 상황(exception 에 대한 메시지)에 대한 메시지를 출력하겠다는 이 메서드명을 봤을 때 - // 파라미터는 exception 자체를 넣어주는 것이 더 자연스럽지 않을까 한다. outputHandler.showExceptionMessage(e); } catch (Exception e) { outputHandler.showSimpleMessage("프로그램에 문제가 생겼습니다."); @@ -103,7 +102,7 @@ private String getUserActionInputFromUser() { private CellPosition getCellInputFromUser() { outputHandler.showCommentForSelectingCell(); CellPosition cellPosition = inputHandler.getCellPositionFromUser(); - if (gameBoard.isInValidCellPosition(cellPosition)) { + if (gameBoard.isInvalidCellPosition(cellPosition)) { throw new GameException("잘못된 좌표를 선택하셨습니다."); } @@ -127,4 +126,5 @@ private void checkIfGameIsOver() { private void changeGameStatusToWin() { gameStatus = 1; } + } 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..252fe78c2 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/cell/Cells.java @@ -0,0 +1,28 @@ +package cleancode.minesweeper.tobe.cell; + +import java.util.Arrays; +import java.util.List; + +public class Cells { + private final List cells; + + private Cells(List cells) { + this.cells = cells; + } + + public static Cells of(List cells) { + return new Cells(cells); + } + + public static Cells from(Cell[][] cells) { + List cellList = Arrays.stream(cells) + .flatMap(Arrays::stream) + .toList(); + return of(cellList); + } + + public boolean isAllChecked() { + return cells.stream() + .allMatch(Cell::isChecked); + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java index 157870c80..b0074d8ed 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -40,7 +40,7 @@ private String generateColAlphabets(GameBoard board) { } @Override - public void showPrintGameWinningComment() { + public void showGameWinningComment() { System.out.println("지뢰를 모두 찾았습니다. GAME CLEAR!"); } diff --git a/src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java index 7552cadd2..80bd7f7c8 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java @@ -14,7 +14,7 @@ public interface OutputHandler { void showBoard(GameBoard board); - void showPrintGameWinningComment(); + void showGameWinningComment(); void showGameLosingComment(); diff --git a/src/main/java/cleancode/minesweeper/tobe/positoion/CellPosition.java b/src/main/java/cleancode/minesweeper/tobe/positoion/CellPosition.java index 94e0bee25..86b1332dc 100644 --- a/src/main/java/cleancode/minesweeper/tobe/positoion/CellPosition.java +++ b/src/main/java/cleancode/minesweeper/tobe/positoion/CellPosition.java @@ -19,10 +19,10 @@ public static CellPosition of(int rowIndex, int colIndex) { } @Override - public boolean equals(Object object) { - if (this == object) return true; - if (object == null || getClass() != object.getClass()) return false; - CellPosition that = (CellPosition) object; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CellPosition that = (CellPosition) o; return rowIndex == that.rowIndex && colIndex == that.colIndex; } @@ -40,16 +40,16 @@ public boolean isColIndexMoreThanOrEqual(int colIndex) { } public int getRowIndex() { - return this.rowIndex; + return rowIndex; } public int getColIndex() { - return this.colIndex; + return colIndex; } public boolean canCalculatePositionBy(RelativePosition relativePosition) { - return this.getRowIndex() + relativePosition.getDeltaRow() >= 0 - && this.getColIndex() + relativePosition.getDeltaCol() >= 0; + return this.rowIndex + relativePosition.getDeltaRow() >= 0 + && this.colIndex + relativePosition.getDeltaCol() >= 0; } public CellPosition calculatePositionBy(RelativePosition relativePosition) { @@ -59,7 +59,7 @@ public CellPosition calculatePositionBy(RelativePosition relativePosition) { this.colIndex + relativePosition.getDeltaCol() ); } - throw new IllegalArgumentException("움직일 수 없는 좌표입니다."); + throw new IllegalArgumentException("움직일 수 있는 좌표가 아닙니다."); } public boolean isRowIndexLessThan(int rowIndex) { @@ -69,4 +69,5 @@ public boolean isRowIndexLessThan(int rowIndex) { public boolean isColIndexLessThan(int colIndex) { return this.colIndex < colIndex; } + } diff --git a/src/main/java/cleancode/minesweeper/tobe/positoion/CellPositions.java b/src/main/java/cleancode/minesweeper/tobe/positoion/CellPositions.java new file mode 100644 index 000000000..c2c454be1 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/positoion/CellPositions.java @@ -0,0 +1,57 @@ +package cleancode.minesweeper.tobe.positoion; + +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) { + List cellPositions = new ArrayList<>(positions); + + Collections.shuffle(cellPositions); + return cellPositions.subList(0, count); + } + + public List subtract(List positionListToSubtract) { + List cellPositions = new ArrayList<>(positions); + CellPositions positionsToSubtract = CellPositions.of(positionListToSubtract); + + return cellPositions.stream() + .filter(positionsToSubtract::doesNotContain) + .toList(); + } + + private boolean doesNotContain(CellPosition position) { + return !positions.contains(position); + } + + public List getPositions() { + return new ArrayList<>(positions); + } +} diff --git a/src/main/java/cleancode/minesweeper/tobe/positoion/RelativePosition.java b/src/main/java/cleancode/minesweeper/tobe/positoion/RelativePosition.java index 8989080a9..93831a35d 100644 --- a/src/main/java/cleancode/minesweeper/tobe/positoion/RelativePosition.java +++ b/src/main/java/cleancode/minesweeper/tobe/positoion/RelativePosition.java @@ -4,10 +4,11 @@ import java.util.Objects; public class RelativePosition { + public static final List SURROUNDED_POSITIONS = List.of( RelativePosition.of(-1, -1), RelativePosition.of(-1, 0), - RelativePosition.of(-1, -1), + RelativePosition.of(-1, 1), RelativePosition.of(0, -1), RelativePosition.of(0, 1), RelativePosition.of(1, -1), @@ -28,10 +29,10 @@ public static RelativePosition of(int deltaRow, int deltaCol) { } @Override - public boolean equals(Object object) { - if (this == object) return true; - if (object == null || getClass() != object.getClass()) return false; - RelativePosition that = (RelativePosition) object; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RelativePosition that = (RelativePosition) o; return deltaRow == that.deltaRow && deltaCol == that.deltaCol; } @@ -41,10 +42,11 @@ public int hashCode() { } public int getDeltaRow() { - return this.deltaRow; + return deltaRow; } public int getDeltaCol() { - return this.deltaCol; + return deltaCol; } + } From 15e938f9790f58952b8a67e5cd256bea0898753c Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Thu, 13 Mar 2025 22:36:29 +0900 Subject: [PATCH 18/30] =?UTF-8?q?feat(=EA=B0=9D=EC=B2=B4-=EC=A7=80?= =?UTF-8?q?=ED=96=A5-=EC=A0=81=EC=9A=A9=ED=95=98=EA=B8=B0):=20Enum=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20+=20Cell=20=EC=9D=98=20CellSign=20?= =?UTF-8?q?=EC=B1=85=EC=9E=84=EC=9D=84=20ConsoleOutputHandler=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EC=A0=84=20->=20CellShapshout=20VO=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleancode/minesweeper/tobe/GameBoard.java | 11 +++- .../minesweeper/tobe/Minesweeper.java | 17 +++--- .../cleancode/minesweeper/tobe/cell/Cell.java | 7 +-- .../minesweeper/tobe/cell/CellSnapshot.java | 58 +++++++++++++++++++ .../tobe/cell/CellSnapshotStatus.java | 16 +++++ .../minesweeper/tobe/cell/EmptyCell.java | 9 ++- .../minesweeper/tobe/cell/LandMineCell.java | 10 ++-- .../minesweeper/tobe/cell/NumberCell.java | 9 ++- .../tobe/io/ConsoleInputHandler.java | 13 ++++- .../tobe/io/ConsoleOutputHandler.java | 32 +++++++++- .../minesweeper/tobe/io/InputHandler.java | 3 +- .../minesweeper/tobe/user/UserAction.java | 11 ++++ 12 files changed, 161 insertions(+), 35 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 a021cfcdc..7d5d01454 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -1,6 +1,7 @@ package cleancode.minesweeper.tobe; import cleancode.minesweeper.tobe.cell.Cell; +import cleancode.minesweeper.tobe.cell.CellSnapshot; import cleancode.minesweeper.tobe.cell.Cells; import cleancode.minesweeper.tobe.cell.EmptyCell; import cleancode.minesweeper.tobe.cell.LandMineCell; @@ -118,11 +119,16 @@ private void updateCellAt(CellPosition position, Cell cell) { board[position.getRowIndex()][position.getColIndex()] = cell; } - public String getSign(CellPosition cellPosition) { + public CellSnapshot getSnapshot(CellPosition cellPosition) { Cell cell = findCell(cellPosition); - return cell.getSign(); + return cell.getSnapshot(); } +// public String getSign(CellPosition cellPosition) { +// Cell cell = findCell(cellPosition); +// return cell.getSign(); +// } + private Cell findCell(CellPosition cellPosition) { return board[cellPosition.getRowIndex()][cellPosition.getColIndex()]; } @@ -154,5 +160,4 @@ private List calculateSurroundedPositions(CellPosition cellPositio .filter(position -> position.isColIndexLessThan(colSize)) .toList(); } - } diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java index f649a6e18..364135eb8 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.positoion.CellPosition; +import cleancode.minesweeper.tobe.user.UserAction; public class Minesweeper implements GameInitializable, GameRunnable { @@ -51,7 +52,7 @@ public void run() { } CellPosition cellPosition = getCellInputFromUser(); - String userActionInput = getUserActionInputFromUser(); + UserAction userActionInput = getUserActionInputFromUser(); actOnCell(cellPosition, userActionInput); } catch (GameException e) { outputHandler.showExceptionMessage(e); @@ -61,7 +62,7 @@ public void run() { } } - private void actOnCell(CellPosition cellPosition, String userActionInput) { + private void actOnCell(CellPosition cellPosition, UserAction userActionInput) { if (doesUserChooseToPlantFlag(userActionInput)) { gameBoard.flagAt(cellPosition); checkIfGameIsOver(); @@ -86,17 +87,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 7b8d9bcbb..15616d736 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/Cell.java @@ -2,13 +2,12 @@ public interface Cell { - // 하위 클래스에서도 사용할 수 있기 때문에 protected 로 변경 - String FLAG_SIGN = "⚑"; - String UNCHECKED_SIGN = "□"; + boolean isLandMine(); boolean hasLandMineCount(); - String getSign(); + + CellSnapshot getSnapshot(); // isOpened, isFlagged 는 Cell 의 공통 기능이므로 그대로 둔다. void flag(); 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..f9f65e482 --- /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, 5); + } + + public CellSnapshotStatus getStatus() { + return status; + } + + public int getNearbyLandMineCount() { + return nearbyLandMineCount; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + CellSnapshot that = (CellSnapshot) object; + return getNearbyLandMineCount() == that.getNearbyLandMineCount() && getStatus() == that.getStatus(); + } + + @Override + public int hashCode() { + return Objects.hash(getStatus(), getNearbyLandMineCount()); + } +} 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..5418bc90e --- /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 0021c03e1..168d7cfd0 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/EmptyCell.java @@ -2,7 +2,6 @@ public class EmptyCell implements Cell { - private static final String EMPTY_SIGN = "■"; private final CellState cellState = CellState.initialize(); @@ -17,14 +16,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 e79b94774..f7ce6ae02 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/LandMineCell.java @@ -2,7 +2,6 @@ public class LandMineCell implements Cell { - private static final String LAND_MINE_SIGN = "☼"; private final CellState cellState = CellState.initialize(); @Override @@ -16,15 +15,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 4c2431d1f..d9229c98e 100644 --- a/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/cell/NumberCell.java @@ -21,15 +21,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 23e5b4455..6f4ab02cb 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.positoion.CellPosition; +import cleancode.minesweeper.tobe.user.UserAction; import java.util.Scanner; @@ -10,8 +11,16 @@ 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 b0074d8ed..5fe3f43ba 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -2,12 +2,18 @@ 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.positoion.CellPosition; import java.util.List; import java.util.stream.IntStream; public class ConsoleOutputHandler implements OutputHandler { + private static final String EMPTY_SIGN = "■"; + private static final String LAND_MINE_SIGN = "☼"; + private static final String FLAG_SIGN = "⚑"; + private static final String UNCHECKED_SIGN = "□"; @Override public void showGameStartComments() { @@ -25,12 +31,36 @@ public void showBoard(GameBoard board) { System.out.printf("%2d ", row + 1); for (int col = 0; col < board.getColSize(); col++) { CellPosition cellPosition = CellPosition.of(row, col); - System.out.print(board.getSign(cellPosition) + " "); + + CellSnapshot snapshot = board.getSnapshot(cellPosition); + String cellSign = decideCellSignFrom(snapshot); + + System.out.print(cellSign + " "); } System.out.println(); } } + private static 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("확인할 수 없는 셀입니다."); + } + private String generateColAlphabets(GameBoard board) { List alphabets = IntStream.range(0, board.getColSize()) .mapToObj(index -> (char) (index + 'a')) diff --git a/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java index cc464f241..dfab7dc6f 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java @@ -1,9 +1,10 @@ package cleancode.minesweeper.tobe.io; import cleancode.minesweeper.tobe.positoion.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..64a19afec --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/user/UserAction.java @@ -0,0 +1,11 @@ +package cleancode.minesweeper.tobe.user; + +public enum UserAction { + OPEN("셀 열기"), FLAG("깃발 꽂기"), UNKNOWN("알 수 없음"); + + private final String description; + + UserAction(String description) { + this.description = description; + } +} From 0b1a96cdd8d8de3e7a674ef426267ea6c537f6ff Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Thu, 13 Mar 2025 23:30:34 +0900 Subject: [PATCH 19/30] =?UTF-8?q?feat(=EA=B0=9D=EC=B2=B4-=EC=A7=80?= =?UTF-8?q?=ED=96=A5-=EC=A0=81=EC=9A=A9=ED=95=98=EA=B8=B0-->-=EB=8B=A4?= =?UTF-8?q?=ED=98=95=EC=84=B1-=ED=99=9C=EC=9A=A9=ED=95=98=EA=B8=B0):=20int?= =?UTF-8?q?erface=20=EC=9D=B4=EC=9A=A9=ED=95=98=EA=B8=B0=20->=20CellSignPr?= =?UTF-8?q?ovidable=20=EC=9D=84=20=ED=86=B5=ED=95=B4=20Sign=20=EB=93=A4?= =?UTF-8?q?=EC=9D=84=20=EA=B0=9D=EC=B2=B4=ED=99=94=ED=96=88=EA=B3=A0,=20Ce?= =?UTF-8?q?llSnapshot=20VO=20=EB=A5=BC=20=ED=86=B5=ED=95=B4=20Sign=20?= =?UTF-8?q?=EC=9D=84=20=EA=B5=AC=EB=B6=84=EC=8B=9C=ED=82=A8=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/GameApplication.java | 3 +- .../minesweeper/tobe/cell/CellSnapshot.java | 4 +++ .../tobe/io/ConsoleOutputHandler.java | 30 +++---------------- .../tobe/io/sign/CellSignFinder.java | 23 ++++++++++++++ .../tobe/io/sign/CellSignProvidable.java | 9 ++++++ .../tobe/io/sign/EmptyCellSignProvider.java | 19 ++++++++++++ .../tobe/io/sign/FlagCellSignProvider.java | 18 +++++++++++ .../io/sign/LandMineCellSignProvider.java | 19 ++++++++++++ .../tobe/io/sign/NumberCellSignProvider.java | 16 ++++++++++ .../io/sign/UncheckedCellSignProvider.java | 18 +++++++++++ 10 files changed, 132 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/GameApplication.java b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java index 9ad5102db..909af52c2 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java @@ -1,6 +1,7 @@ package cleancode.minesweeper.tobe; import cleancode.minesweeper.tobe.gamelevel.Advanced; +import cleancode.minesweeper.tobe.gamelevel.Beginner; import cleancode.minesweeper.tobe.gamelevel.GameLevel; import cleancode.minesweeper.tobe.gamelevel.Middle; import cleancode.minesweeper.tobe.io.ConsoleInputHandler; @@ -13,7 +14,7 @@ public class GameApplication { // 이름도 MinesweeperGame 에서 GameApplication 으로 변경한다. -> 이렇게 변경하면 지뢰찾기게임(Minesweeper 뿐만이 아닌 다른 게임도 실행할 수 있게 된다.) // 게임 실행에 대한 책임과 지뢰찾기 도메인 자체, 지뢰찾기 게임을 담당하는 역할을 분리했다. public static void main(String[] args) { - GameLevel gameLevel = new Middle(); + GameLevel gameLevel = new Beginner(); InputHandler inputHandler = new ConsoleInputHandler(); ConsoleOutputHandler outputHandler = new ConsoleOutputHandler(); diff --git a/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshot.java b/src/main/java/cleancode/minesweeper/tobe/cell/CellSnapshot.java index f9f65e482..dedeb6cac 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, 5); } + public boolean isSameStatus(CellSnapshotStatus cellSnapshotStatus) { + return this.status == cellSnapshotStatus; + } + public CellSnapshotStatus getStatus() { return status; } diff --git a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java index 5fe3f43ba..40fdc3272 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -3,17 +3,15 @@ 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.CellSignFinder; import cleancode.minesweeper.tobe.positoion.CellPosition; import java.util.List; import java.util.stream.IntStream; public class ConsoleOutputHandler implements OutputHandler { - private static final String EMPTY_SIGN = "■"; - private static final String LAND_MINE_SIGN = "☼"; - private static final String FLAG_SIGN = "⚑"; - private static final String UNCHECKED_SIGN = "□"; + + private final CellSignFinder cellSignFinder = new CellSignFinder(); @Override public void showGameStartComments() { @@ -33,7 +31,7 @@ public void showBoard(GameBoard board) { CellPosition cellPosition = CellPosition.of(row, col); CellSnapshot snapshot = board.getSnapshot(cellPosition); - String cellSign = decideCellSignFrom(snapshot); + String cellSign = cellSignFinder.findCellSignFrom(snapshot); System.out.print(cellSign + " "); } @@ -41,26 +39,6 @@ public void showBoard(GameBoard board) { } } - private static 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("확인할 수 없는 셀입니다."); - } - private String generateColAlphabets(GameBoard board) { List alphabets = IntStream.range(0, board.getColSize()) .mapToObj(index -> (char) (index + '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..5b0dfe647 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignFinder.java @@ -0,0 +1,23 @@ +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)) + .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..2ba61f129 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvidable.java @@ -0,0 +1,9 @@ +package cleancode.minesweeper.tobe.io.sign; + +import cleancode.minesweeper.tobe.cell.CellSnapshot; + +public interface CellSignProvidable { + + boolean supports(CellSnapshot cellSnapshot); + String provide(CellSnapshot cellSnapshot); +} 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..7ed62e302 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/EmptyCellSignProvider.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 EmptyCellSignProvider implements CellSignProvidable{ + + private 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..8719e01ba --- /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{ + private static final 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..1da8a1ada --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/LandMineCellSignProvider.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 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..25e71d2e8 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/NumberCellSignProvider.java @@ -0,0 +1,16 @@ +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..30f5b4e73 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/UncheckedCellSignProvider.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 UncheckedCellSignProvider implements CellSignProvidable { + private static final String UNCHECKED_SIGN = "□"; + + @Override + public boolean supports(CellSnapshot cellSnapshot) { + return cellSnapshot.isSameStatus(CellSnapshotStatus.UNCHECKED); + } + + @Override + public String provide(CellSnapshot cellSnapshot) { + return UNCHECKED_SIGN; + } +} From a8f63420d37ea27edea2fc845f45e6a733794616 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Fri, 14 Mar 2025 01:23:27 +0900 Subject: [PATCH 20/30] =?UTF-8?q?feat(=EB=8B=A4=ED=98=95=EC=84=B1-?= =?UTF-8?q?=ED=99=9C=EC=9A=A9=ED=95=98=EA=B8=B0-->-CellSnapshotStatus-?= =?UTF-8?q?=EA=B0=80-=EC=B6=94=EA=B0=80=EB=90=A0-=EB=95=8C=EB=A7=88?= =?UTF-8?q?=EB=8B=A4-CellSignFinder-=EC=97=90=EB=8F=84-=EC=B6=94=EA=B0=80?= =?UTF-8?q?=ED=95=B4=EC=95=BC-=ED=95=9C=EB=8B=A4):=20=ED=95=B4=EA=B2=B0?= =?UTF-8?q?=EB=B0=A9=EB=B2=95:=20CellSignProvider=20Enum=20=EC=9E=90?= =?UTF-8?q?=EC=B2=B4=EA=B0=80=20interface=20=EB=A5=BC=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=ED=95=A8=EC=9C=BC=EB=A1=9C=EC=8D=A8=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=EC=B2=B4=EB=8F=84=20=EA=B0=96=EA=B3=A0=20=EC=9E=88=EA=B3=A0,?= =?UTF-8?q?=20=EC=B6=94=EC=83=81=ED=99=94=EB=90=9C=20=EC=8A=A4=ED=8E=99?= =?UTF-8?q?=EB=8F=84=20=EA=B0=99=EC=9D=B4=20=EA=B0=96=EA=B3=A0=20=EC=9E=88?= =?UTF-8?q?=EB=8A=94=20=ED=98=95=ED=83=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tobe/io/ConsoleOutputHandler.java | 4 +- .../tobe/io/sign/CellSignProvider.java | 69 +++++++++++++++++++ 2 files changed, 72 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 40fdc3272..595e26408 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java @@ -4,6 +4,7 @@ import cleancode.minesweeper.tobe.GameException; import cleancode.minesweeper.tobe.cell.CellSnapshot; import cleancode.minesweeper.tobe.io.sign.CellSignFinder; +import cleancode.minesweeper.tobe.io.sign.CellSignProvider; import cleancode.minesweeper.tobe.positoion.CellPosition; import java.util.List; @@ -31,7 +32,8 @@ public void showBoard(GameBoard board) { 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 + " "); } 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..c721d0a0d --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/io/sign/CellSignProvider.java @@ -0,0 +1,69 @@ +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; + } + }, + LAND_MINE(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()); + } + }, + UNKNOWN(CellSnapshotStatus.UNCHECKED) { + @Override + public String provide(CellSnapshot cellSnapshot) { + return UNCHECKED_SIGN; + } + } + ; + + + 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 = "□"; + + private final CellSnapshotStatus status; + + CellSignProvider(CellSnapshotStatus status) { + this.status = status; + } + + @Override + public boolean supports(CellSnapshot cellSnapshot) { + return cellSnapshot.isSameStatus(status); + } + + public static String findCellSignFrom(CellSnapshot snapshot) { + CellSignProvider cellSignProvider = findBy(snapshot); + return cellSignProvider.provide(snapshot); + } + + private static CellSignProvider findBy(CellSnapshot snapshot) { + return Arrays.stream(values()) + .filter(provider -> provider.supports(snapshot)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("확인할 수 없는 셀입니다.")); + } +} From 7a91abb2478fbafc08e9f6aba459a344532b21ca Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Fri, 14 Mar 2025 01:45:09 +0900 Subject: [PATCH 21/30] =?UTF-8?q?feat(=EA=B0=9D=EC=B2=B4-=EC=A7=80?= =?UTF-8?q?=ED=96=A5-=EC=A0=81=EC=9A=A9=ED=95=98=EA=B8=B0):=20=EC=88=A8?= =?UTF-8?q?=EA=B2=A8=EC=A0=B8=20=EC=9E=88=EB=8A=94=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EA=B0=9C=EB=85=90=20=EB=8F=84=EC=B6=9C=ED=95=98?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/GameApplication.java | 7 ++--- .../minesweeper/tobe/Minesweeper.java | 9 +++--- .../minesweeper/tobe/config/GameConfig.java | 29 +++++++++++++++++++ 3 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/config/GameConfig.java diff --git a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java index 909af52c2..718184eca 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; @@ -14,11 +15,9 @@ public class GameApplication { // 이름도 MinesweeperGame 에서 GameApplication 으로 변경한다. -> 이렇게 변경하면 지뢰찾기게임(Minesweeper 뿐만이 아닌 다른 게임도 실행할 수 있게 된다.) // 게임 실행에 대한 책임과 지뢰찾기 도메인 자체, 지뢰찾기 게임을 담당하는 역할을 분리했다. public static void main(String[] args) { - GameLevel gameLevel = new Beginner(); - InputHandler inputHandler = new ConsoleInputHandler(); - ConsoleOutputHandler outputHandler = new ConsoleOutputHandler(); + GameConfig gameConfig = new GameConfig(new Beginner(), new ConsoleInputHandler(), new ConsoleOutputHandler()); - Minesweeper minesweeper = new Minesweeper(gameLevel, inputHandler, outputHandler); + 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 364135eb8..2cac7f47b 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; @@ -23,10 +24,10 @@ public class Minesweeper implements GameInitializable, GameRunnable { 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; + public Minesweeper(GameConfig gameConfig) { + gameBoard = new GameBoard(gameConfig.getGameLevel()); + this.inputHandler = gameConfig.getInputHandler(); + this.outputHandler = gameConfig.getOutputHandler(); } @Override 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..658dc9f64 --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/config/GameConfig.java @@ -0,0 +1,29 @@ +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; + } +} From f8a1e0bf128db54354fdddd7174fb3944cd3f762 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Sat, 15 Mar 2025 16:45:29 +0900 Subject: [PATCH 22/30] =?UTF-8?q?feat(=EC=BD=94=EB=93=9C-=EB=8B=A4?= =?UTF-8?q?=EB=93=AC=EA=B8=B0):=20=EC=A3=BC=EC=84=9D=EC=9D=98=20=EC=96=91?= =?UTF-8?q?=EB=A9=B4=EC=84=B1=20>=20int=20gameStatus=20>=20enum=20GameStat?= =?UTF-8?q?us=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/GameBoard.java | 69 +++++++++++++++---- .../minesweeper/tobe/GameStatus.java | 14 ++++ .../minesweeper/tobe/Minesweeper.java | 55 +++------------ 3 files changed, 79 insertions(+), 59 deletions(-) create mode 100644 src/main/java/cleancode/minesweeper/tobe/GameStatus.java diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index 7d5d01454..975fa2c56 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -16,6 +16,7 @@ public class GameBoard { private final Cell[][] board; private final int landMineCount; + private GameStatus gameStatus; public GameBoard(GameLevel gameLevel) { int rowSize = gameLevel.getRowSize(); @@ -23,14 +24,51 @@ public GameBoard(GameLevel gameLevel) { board = new Cell[rowSize][colSize]; landMineCount = gameLevel.getLandMineCount(); + initializeGameStatus(); + } + + public void initializeGame() { + initializeGameStatus(); + CellPositions cellPositions = CellPositions.from(board); + + initializeEmptyCells(cellPositions); + + List landMinePositions = cellPositions.extractRandomPositions(landMineCount); + initializeLandMineCells(landMinePositions); + + List numberPositionCandidates = cellPositions.subtract(landMinePositions); + initializeNumberCells(numberPositionCandidates); } 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 void openAt(CellPosition cellPosition) { + if (isLandMineCellAt(cellPosition)) { + openOneCellAt(cellPosition); + changeGameStatusToLose(); + return; + } + + openSurroundedCells(cellPosition); + checkIfGameIsOver(); + } + + public void openOneCellAt(CellPosition cellPosition) { Cell cell = findCell(cellPosition); cell.open(); } @@ -43,7 +81,7 @@ public void openSurroundedCells(CellPosition cellPosition) { return; } - openAt(cellPosition); + openOneCellAt(cellPosition); if (doesCellHaveLandMineCount(cellPosition)) { return; @@ -81,16 +119,16 @@ public boolean isInvalidCellPosition(CellPosition cellPosition) { || cellPosition.isColIndexMoreThanOrEqual(colSize); } - public void initializeGame() { - CellPositions cellPositions = CellPositions.from(board); - - initializeEmptyCells(cellPositions); + public boolean inInProgress() { + return gameStatus == GameStatus.IN_PROGRESS; + } - List landMinePositions = cellPositions.extractRandomPositions(landMineCount); - initializeLandMineCells(landMinePositions); + private void initializeGameStatus() { + gameStatus = GameStatus.IN_PROGRESS; + } - List numberPositionCandidates = cellPositions.subtract(landMinePositions); - initializeNumberCells(numberPositionCandidates); + private void changeGameStatusToLose() { + gameStatus = GameStatus.LOSE; } private void initializeEmptyCells(CellPositions cellPositions) { @@ -124,11 +162,6 @@ public CellSnapshot getSnapshot(CellPosition cellPosition) { return cell.getSnapshot(); } -// public String getSign(CellPosition cellPosition) { -// Cell cell = findCell(cellPosition); -// return cell.getSign(); -// } - private Cell findCell(CellPosition cellPosition) { return board[cellPosition.getRowIndex()][cellPosition.getColIndex()]; } @@ -160,4 +193,12 @@ private List calculateSurroundedPositions(CellPosition cellPositio .filter(position -> position.isColIndexLessThan(colSize)) .toList(); } + + public boolean isWinStatus() { + return gameStatus == GameStatus.WIN; + } + + public boolean isLoseStatus() { + return gameStatus == GameStatus.LOSE; + } } 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..4953a99ae --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/GameStatus.java @@ -0,0 +1,14 @@ +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 2cac7f47b..d6a905182 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.positoion.CellPosition; @@ -22,7 +21,6 @@ public class Minesweeper implements GameInitializable, GameRunnable { // 구현체가 변경되어도 Minesweeper 클래스는 영향을 받지 않는다. private final InputHandler inputHandler; private final OutputHandler outputHandler; - private int gameStatus = 0; // 0: 게임 중, 1: 승리, -1: 패배 public Minesweeper(GameConfig gameConfig) { gameBoard = new GameBoard(gameConfig.getGameLevel()); @@ -39,19 +37,10 @@ public void initialize() { public void run() { outputHandler.showGameStartComments(); - while (true) { + while (gameBoard.inInProgress()) { try { outputHandler.showBoard(gameBoard); - if (doesUserWinTheGame()) { - outputHandler.showGameWinningComment(); - break; - } - if (doesUserLoseTheGame()) { - outputHandler.showGameLosingComment(); - break; - } - CellPosition cellPosition = getCellInputFromUser(); UserAction userActionInput = getUserActionInputFromUser(); actOnCell(cellPosition, userActionInput); @@ -61,33 +50,28 @@ public void run() { outputHandler.showSimpleMessage("프로그램에 문제가 생겼습니다."); } } + outputHandler.showBoard(gameBoard); + + if (gameBoard.isWinStatus()) { + outputHandler.showGameWinningComment(); + } + if (gameBoard.isLoseStatus()) { + outputHandler.showGameLosingComment(); + } } private void actOnCell(CellPosition cellPosition, UserAction userActionInput) { if (doesUserChooseToPlantFlag(userActionInput)) { gameBoard.flagAt(cellPosition); - checkIfGameIsOver(); return; } - if (doesUserChooseToOpenCell(userActionInput)) { - if (gameBoard.isLandMineCellAt(cellPosition)) { - gameBoard.openAt(cellPosition); - changeGameStatusToLose(); - return; - } - - gameBoard.openSurroundedCells(cellPosition); - checkIfGameIsOver(); + gameBoard.openAt(cellPosition); return; } throw new GameException("잘못된 번호를 선택하셨습니다."); } - private void changeGameStatusToLose() { - gameStatus = -1; - } - private boolean doesUserChooseToOpenCell(UserAction userAction) { return userAction == UserAction.OPEN; } @@ -110,23 +94,4 @@ private CellPosition getCellInputFromUser() { return cellPosition; } - - private boolean doesUserLoseTheGame() { - return gameStatus == -1; - } - - private boolean doesUserWinTheGame() { - return gameStatus == 1; - } - - private void checkIfGameIsOver() { - if (gameBoard.isAllCellChecked()) { - changeGameStatusToWin(); - } - } - - private void changeGameStatusToWin() { - gameStatus = 1; - } - } From b1659ed32a438ee455b570515c8f3eca2528caf0 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Sat, 15 Mar 2025 17:49:25 +0900 Subject: [PATCH 23/30] =?UTF-8?q?feat(=EC=BD=94=EB=93=9C-=EB=8B=A4?= =?UTF-8?q?=EB=93=AC=EA=B8=B0):=20=EB=B3=80=EC=88=98=EC=99=80=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=EC=9D=98=20=EB=82=98=EC=97=B4=20=EC=88=9C?= =?UTF-8?q?=EC=84=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cleancode/minesweeper/tobe/GameBoard.java | 172 +++++++++--------- .../minesweeper/tobe/Minesweeper.java | 34 ++-- 2 files changed, 107 insertions(+), 99 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java index 975fa2c56..eccaa4d0a 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameBoard.java @@ -27,6 +27,8 @@ public GameBoard(GameLevel gameLevel) { initializeGameStatus(); } + // public + // 상태 변경 public void initializeGame() { initializeGameStatus(); CellPositions cellPositions = CellPositions.from(board); @@ -40,23 +42,6 @@ public void initializeGame() { initializeNumberCells(numberPositionCandidates); } - 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 void openAt(CellPosition cellPosition) { if (isLandMineCellAt(cellPosition)) { openOneCellAt(cellPosition); @@ -68,69 +53,56 @@ public void openAt(CellPosition cellPosition) { checkIfGameIsOver(); } - public void openOneCellAt(CellPosition cellPosition) { + public void flagAt(CellPosition cellPosition) { Cell cell = findCell(cellPosition); - cell.open(); - } - - public void openSurroundedCells(CellPosition cellPosition) { - if (isOpenedCell(cellPosition)) { - return; - } - if (isLandMineCellAt(cellPosition)) { - return; - } + cell.flag(); - openOneCellAt(cellPosition); + checkIfGameIsOver(); + } - if (doesCellHaveLandMineCount(cellPosition)) { - return; - } + // 판별 + public boolean isInvalidCellPosition(CellPosition cellPosition) { + int rowSize = getRowSize(); + int colSize = getColSize(); - List surroundedPositions = calculateSurroundedPositions(cellPosition, getRowSize(), getColSize()); - surroundedPositions.forEach(this::openSurroundedCells); + return cellPosition.isRowIndexMoreThanOrEqual(rowSize) + || cellPosition.isColIndexMoreThanOrEqual(colSize); } - private boolean doesCellHaveLandMineCount(CellPosition cellPosition) { - Cell cell = findCell(cellPosition); - return cell.hasLandMineCount(); + public boolean inInProgress() { + return gameStatus == GameStatus.IN_PROGRESS; } - private boolean isOpenedCell(CellPosition cellPosition) { - Cell cell = findCell(cellPosition); - return cell.isOpened(); - } - public boolean isLandMineCellAt(CellPosition cellPosition) { + // 조회 + public CellSnapshot getSnapshot(CellPosition cellPosition) { Cell cell = findCell(cellPosition); - return cell.isLandMine(); + return cell.getSnapshot(); } - public boolean isAllCellChecked() { - Cells cells = Cells.from(board); - return cells.isAllChecked(); + public int getRowSize() { + return board.length; } - public boolean isInvalidCellPosition(CellPosition cellPosition) { - int rowSize = getRowSize(); - int colSize = getColSize(); + public int getColSize() { + return board[0].length; + } - return cellPosition.isRowIndexMoreThanOrEqual(rowSize) - || cellPosition.isColIndexMoreThanOrEqual(colSize); + public boolean isWinStatus() { + return gameStatus == GameStatus.WIN; } - public boolean inInProgress() { - return gameStatus == GameStatus.IN_PROGRESS; + public boolean isLoseStatus() { + return gameStatus == GameStatus.LOSE; } + + // private + private void initializeGameStatus() { gameStatus = GameStatus.IN_PROGRESS; } - private void changeGameStatusToLose() { - gameStatus = GameStatus.LOSE; - } - private void initializeEmptyCells(CellPositions cellPositions) { List allPositions = cellPositions.getPositions(); for (CellPosition position : allPositions) { @@ -153,27 +125,6 @@ private void initializeNumberCells(List numberPositionCandidates) } } - private void updateCellAt(CellPosition position, Cell cell) { - board[position.getRowIndex()][position.getColIndex()] = cell; - } - - public CellSnapshot getSnapshot(CellPosition cellPosition) { - Cell cell = findCell(cellPosition); - return cell.getSnapshot(); - } - - private Cell findCell(CellPosition cellPosition) { - return board[cellPosition.getRowIndex()][cellPosition.getColIndex()]; - } - - public int getRowSize() { - return board.length; - } - - public int getColSize() { - return board[0].length; - } - private int countNearbyLandMines(CellPosition cellPosition) { int rowSize = getRowSize(); int colSize = getColSize(); @@ -194,11 +145,68 @@ private List calculateSurroundedPositions(CellPosition cellPositio .toList(); } - public boolean isWinStatus() { - return gameStatus == GameStatus.WIN; + private void updateCellAt(CellPosition position, Cell cell) { + board[position.getRowIndex()][position.getColIndex()] = cell; } - public boolean isLoseStatus() { - return gameStatus == GameStatus.LOSE; + private void openSurroundedCells(CellPosition cellPosition) { + if (isOpenedCell(cellPosition)) { + return; + } + if (isLandMineCellAt(cellPosition)) { + return; + } + + openOneCellAt(cellPosition); + + if (doesCellHaveLandMineCount(cellPosition)) { + return; + } + + List surroundedPositions = calculateSurroundedPositions(cellPosition, getRowSize(), getColSize()); + surroundedPositions.forEach(this::openSurroundedCells); + } + + private void openOneCellAt(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); + cell.open(); + } + + private boolean isOpenedCell(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); + return cell.isOpened(); + } + + private boolean isLandMineCellAt(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); + return cell.isLandMine(); + } + + private boolean doesCellHaveLandMineCount(CellPosition cellPosition) { + Cell cell = findCell(cellPosition); + return cell.hasLandMineCount(); + } + + private void checkIfGameIsOver() { + if (isAllCellChecked()) { + changeGameStatusToWin(); + } + } + + private boolean isAllCellChecked() { + Cells cells = Cells.from(board); + return cells.isAllChecked(); + } + + private void changeGameStatusToWin() { + gameStatus = GameStatus.WIN; + } + + private void changeGameStatusToLose() { + 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 d6a905182..cd13c46ee 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java @@ -60,6 +60,21 @@ public void run() { } } + private CellPosition getCellInputFromUser() { + outputHandler.showCommentForSelectingCell(); + CellPosition cellPosition = inputHandler.getCellPositionFromUser(); + if (gameBoard.isInvalidCellPosition(cellPosition)) { + throw new GameException("잘못된 좌표를 선택하셨습니다."); + } + + return cellPosition; + } + + private UserAction getUserActionInputFromUser() { + outputHandler.showCommentForUserAction(); + return inputHandler.getUserActionFromUser(); + } + private void actOnCell(CellPosition cellPosition, UserAction userActionInput) { if (doesUserChooseToPlantFlag(userActionInput)) { gameBoard.flagAt(cellPosition); @@ -72,26 +87,11 @@ private void actOnCell(CellPosition cellPosition, UserAction userActionInput) { throw new GameException("잘못된 번호를 선택하셨습니다."); } - 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(); - if (gameBoard.isInvalidCellPosition(cellPosition)) { - throw new GameException("잘못된 좌표를 선택하셨습니다."); - } - - return cellPosition; + private boolean doesUserChooseToOpenCell(UserAction userAction) { + return userAction == UserAction.OPEN; } } From f755006841d60ab8020bb878b93979f2c251a1cf Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Sat, 15 Mar 2025 18:09:31 +0900 Subject: [PATCH 24/30] =?UTF-8?q?feat(=EC=BD=94=EB=93=9C-=EB=8B=A4?= =?UTF-8?q?=EB=93=AC=EA=B8=B0):=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EB=82=98?= =?UTF-8?q?=EB=88=84=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/GameApplication.java | 13 ++++------ .../minesweeper/tobe/io/InputHandler.java | 10 -------- .../tobe/{ => minesweeper}/Minesweeper.java | 14 ++++++----- .../{ => minesweeper/board}/GameBoard.java | 24 +++++++++---------- .../{ => minesweeper/board}/GameStatus.java | 2 +- .../{ => minesweeper/board}/cell/Cell.java | 2 +- .../board}/cell/CellSnapshot.java | 2 +- .../board}/cell/CellSnapshotStatus.java | 2 +- .../board}/cell/CellState.java | 2 +- .../{ => minesweeper/board}/cell/Cells.java | 2 +- .../board}/cell/EmptyCell.java | 2 +- .../board}/cell/LandMineCell.java | 2 +- .../board}/cell/NumberCell.java | 2 +- .../board}/positoion/CellPosition.java | 2 +- .../board}/positoion/CellPositions.java | 4 ++-- .../board}/positoion/RelativePosition.java | 2 +- .../{ => minesweeper}/config/GameConfig.java | 8 +++---- .../exception}/GameException.java | 2 +- .../{ => minesweeper}/gamelevel/Advanced.java | 2 +- .../{ => minesweeper}/gamelevel/Beginner.java | 2 +- .../gamelevel/GameLevel.java | 2 +- .../{ => minesweeper}/gamelevel/Middle.java | 2 +- .../gamelevel/VeryBeginner.java | 2 +- .../io}/BoardIndexConverter.java | 4 +++- .../io/ConsoleInputHandler.java | 7 +++--- .../io/ConsoleOutputHandler.java | 16 ++++++------- .../tobe/minesweeper/io/InputHandler.java | 10 ++++++++ .../{ => minesweeper}/io/OutputHandler.java | 6 ++--- .../io/sign/CellSignFinder.java | 4 ++-- .../io/sign/CellSignProvidable.java | 4 ++-- .../io/sign/CellSignProvider.java | 6 ++--- .../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 ++--- .../{ => minesweeper}/user/UserAction.java | 2 +- 37 files changed, 98 insertions(+), 98 deletions(-) delete mode 100644 src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/Minesweeper.java (86%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/GameBoard.java (86%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/GameStatus.java (78%) 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 (81%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/cell/CellState.java (91%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/cell/Cells.java (89%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/cell/EmptyCell.java (92%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/cell/LandMineCell.java (92%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/cell/NumberCell.java (93%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/positoion/CellPosition.java (96%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/positoion/CellPositions.java (91%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/board}/positoion/RelativePosition.java (94%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/config/GameConfig.java (69%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/exception}/GameException.java (67%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/gamelevel/Advanced.java (78%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/gamelevel/Beginner.java (78%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/gamelevel/GameLevel.java (92%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/gamelevel/Middle.java (78%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/gamelevel/VeryBeginner.java (79%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper/io}/BoardIndexConverter.java (86%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/ConsoleInputHandler.java (79%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/ConsoleOutputHandler.java (81%) create mode 100644 src/main/java/cleancode/minesweeper/tobe/minesweeper/io/InputHandler.java rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/OutputHandler.java (82%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/sign/CellSignFinder.java (82%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/sign/CellSignProvidable.java (51%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/sign/CellSignProvider.java (89%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/sign/EmptyCellSignProvider.java (62%) 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 (63%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/sign/NumberCellSignProvider.java (62%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/io/sign/UncheckedCellSignProvider.java (63%) rename src/main/java/cleancode/minesweeper/tobe/{ => minesweeper}/user/UserAction.java (79%) diff --git a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java index 718184eca..0d8fdb595 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameApplication.java +++ b/src/main/java/cleancode/minesweeper/tobe/GameApplication.java @@ -1,13 +1,10 @@ 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.gamelevel.Middle; -import cleancode.minesweeper.tobe.io.ConsoleInputHandler; -import cleancode.minesweeper.tobe.io.ConsoleOutputHandler; -import cleancode.minesweeper.tobe.io.InputHandler; +import cleancode.minesweeper.tobe.minesweeper.Minesweeper; +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/io/InputHandler.java b/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java deleted file mode 100644 index dfab7dc6f..000000000 --- a/src/main/java/cleancode/minesweeper/tobe/io/InputHandler.java +++ /dev/null @@ -1,10 +0,0 @@ -package cleancode.minesweeper.tobe.io; - -import cleancode.minesweeper.tobe.positoion.CellPosition; -import cleancode.minesweeper.tobe.user.UserAction; - -public interface InputHandler { - UserAction getUserActionFromUser(); - - CellPosition getCellPositionFromUser(); -} diff --git a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/Minesweeper.java similarity index 86% rename from src/main/java/cleancode/minesweeper/tobe/Minesweeper.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/Minesweeper.java index cd13c46ee..939839d15 100644 --- a/src/main/java/cleancode/minesweeper/tobe/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/Minesweeper.java @@ -1,12 +1,14 @@ -package cleancode.minesweeper.tobe; +package cleancode.minesweeper.tobe.minesweeper; -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.positoion.CellPosition; -import cleancode.minesweeper.tobe.user.UserAction; +import cleancode.minesweeper.tobe.minesweeper.io.InputHandler; +import cleancode.minesweeper.tobe.minesweeper.io.OutputHandler; +import cleancode.minesweeper.tobe.minesweeper.board.positoion.CellPosition; +import cleancode.minesweeper.tobe.minesweeper.user.UserAction; public class Minesweeper implements GameInitializable, GameRunnable { diff --git a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java similarity index 86% rename from src/main/java/cleancode/minesweeper/tobe/GameBoard.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java index eccaa4d0a..fc5a238e6 100644 --- a/src/main/java/cleancode/minesweeper/tobe/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java @@ -1,15 +1,15 @@ -package cleancode.minesweeper.tobe; - -import cleancode.minesweeper.tobe.cell.Cell; -import cleancode.minesweeper.tobe.cell.CellSnapshot; -import cleancode.minesweeper.tobe.cell.Cells; -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 cleancode.minesweeper.tobe.positoion.CellPosition; -import cleancode.minesweeper.tobe.positoion.CellPositions; -import cleancode.minesweeper.tobe.positoion.RelativePosition; +package cleancode.minesweeper.tobe.minesweeper.board; + +import cleancode.minesweeper.tobe.minesweeper.board.cell.Cell; +import cleancode.minesweeper.tobe.minesweeper.board.cell.CellSnapshot; +import cleancode.minesweeper.tobe.minesweeper.board.cell.Cells; +import cleancode.minesweeper.tobe.minesweeper.board.cell.EmptyCell; +import cleancode.minesweeper.tobe.minesweeper.board.cell.LandMineCell; +import cleancode.minesweeper.tobe.minesweeper.board.cell.NumberCell; +import cleancode.minesweeper.tobe.minesweeper.gamelevel.GameLevel; +import cleancode.minesweeper.tobe.minesweeper.board.positoion.CellPosition; +import cleancode.minesweeper.tobe.minesweeper.board.positoion.CellPositions; +import cleancode.minesweeper.tobe.minesweeper.board.positoion.RelativePosition; import java.util.List; public class GameBoard { diff --git a/src/main/java/cleancode/minesweeper/tobe/GameStatus.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameStatus.java similarity index 78% rename from src/main/java/cleancode/minesweeper/tobe/GameStatus.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameStatus.java index 4953a99ae..1b4a1acd2 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 { IN_PROGRESS("진행 중"), 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 15616d736..7b34556bc 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 dedeb6cac..45a59d2fc 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 81% 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 5418bc90e..b40bc74fc 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 { EMPTY("빈 셀"), 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 91% 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 98de7fe51..df13a777f 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 89% 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 252fe78c2..a380f04e3 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 92% 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 168d7cfd0..dcdde7c33 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 { 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 92% 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 f7ce6ae02..a21590217 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 { 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 93% 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 d9229c98e..4003ab44d 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 { diff --git a/src/main/java/cleancode/minesweeper/tobe/positoion/CellPosition.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/positoion/CellPosition.java similarity index 96% rename from src/main/java/cleancode/minesweeper/tobe/positoion/CellPosition.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/positoion/CellPosition.java index 86b1332dc..91457deaf 100644 --- a/src/main/java/cleancode/minesweeper/tobe/positoion/CellPosition.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/positoion/CellPosition.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.positoion; +package cleancode.minesweeper.tobe.minesweeper.board.positoion; import java.util.Objects; diff --git a/src/main/java/cleancode/minesweeper/tobe/positoion/CellPositions.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/positoion/CellPositions.java similarity index 91% rename from src/main/java/cleancode/minesweeper/tobe/positoion/CellPositions.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/positoion/CellPositions.java index c2c454be1..6e157b78f 100644 --- a/src/main/java/cleancode/minesweeper/tobe/positoion/CellPositions.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/positoion/CellPositions.java @@ -1,6 +1,6 @@ -package cleancode.minesweeper.tobe.positoion; +package cleancode.minesweeper.tobe.minesweeper.board.positoion; -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/positoion/RelativePosition.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/positoion/RelativePosition.java similarity index 94% rename from src/main/java/cleancode/minesweeper/tobe/positoion/RelativePosition.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/board/positoion/RelativePosition.java index 93831a35d..089962019 100644 --- a/src/main/java/cleancode/minesweeper/tobe/positoion/RelativePosition.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/positoion/RelativePosition.java @@ -1,4 +1,4 @@ -package cleancode.minesweeper.tobe.positoion; +package cleancode.minesweeper.tobe.minesweeper.board.positoion; 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 69% rename from src/main/java/cleancode/minesweeper/tobe/config/GameConfig.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/config/GameConfig.java index 658dc9f64..b22e1e711 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 { private final GameLevel gameLevel; diff --git a/src/main/java/cleancode/minesweeper/tobe/GameException.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/exception/GameException.java similarity index 67% rename from src/main/java/cleancode/minesweeper/tobe/GameException.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/exception/GameException.java index 2b9bae5b8..aba08dcb0 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 78% rename from src/main/java/cleancode/minesweeper/tobe/gamelevel/Advanced.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/Advanced.java index ed8c30569..774c3ba39 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{ @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 78% rename from src/main/java/cleancode/minesweeper/tobe/gamelevel/Beginner.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/Beginner.java index 4acb00b42..24cc69eac 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{ @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 92% rename from src/main/java/cleancode/minesweeper/tobe/gamelevel/GameLevel.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/GameLevel.java index 82781ea48..59fd3527d 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; // 추상화를 정말 바로 보여주는 구조이다. 인터페이스가 갖고 있는 스펙들 즉, 선언된 메서드 선언부들이 이 객체가 어떠한 역할을 갖는지 설명을 해준다. // 이 GameLevel 인터페이스를 MineSweeper 안에 넣어줄 것이다. 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 78% rename from src/main/java/cleancode/minesweeper/tobe/gamelevel/Middle.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/Middle.java index 7f6892f76..0182d008b 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{ @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 79% rename from src/main/java/cleancode/minesweeper/tobe/gamelevel/VeryBeginner.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/gamelevel/VeryBeginner.java index 2cae9dd3b..8e910853d 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{ diff --git a/src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/BoardIndexConverter.java similarity index 86% rename from src/main/java/cleancode/minesweeper/tobe/BoardIndexConverter.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/BoardIndexConverter.java index b7144491e..3a606f94d 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 79% rename from src/main/java/cleancode/minesweeper/tobe/io/ConsoleInputHandler.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/ConsoleInputHandler.java index 6f4ab02cb..bb4a5fa7b 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.positoion.CellPosition; -import cleancode.minesweeper.tobe.user.UserAction; +import cleancode.minesweeper.tobe.minesweeper.board.positoion.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 81% rename from src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/ConsoleOutputHandler.java index 595e26408..9df42e405 100644 --- a/src/main/java/cleancode/minesweeper/tobe/io/ConsoleOutputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/ConsoleOutputHandler.java @@ -1,11 +1,11 @@ -package cleancode.minesweeper.tobe.io; - -import cleancode.minesweeper.tobe.GameBoard; -import cleancode.minesweeper.tobe.GameException; -import cleancode.minesweeper.tobe.cell.CellSnapshot; -import cleancode.minesweeper.tobe.io.sign.CellSignFinder; -import cleancode.minesweeper.tobe.io.sign.CellSignProvider; -import cleancode.minesweeper.tobe.positoion.CellPosition; +package cleancode.minesweeper.tobe.minesweeper.io; + +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.io.sign.CellSignFinder; +import cleancode.minesweeper.tobe.minesweeper.io.sign.CellSignProvider; +import cleancode.minesweeper.tobe.minesweeper.board.positoion.CellPosition; 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..873514dac --- /dev/null +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/InputHandler.java @@ -0,0 +1,10 @@ +package cleancode.minesweeper.tobe.minesweeper.io; + +import cleancode.minesweeper.tobe.minesweeper.board.positoion.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 82% rename from src/main/java/cleancode/minesweeper/tobe/io/OutputHandler.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/io/OutputHandler.java index 80bd7f7c8..ff8be444e 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 82% 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 5b0dfe647..95ea9948f 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 51% 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 2ba61f129..d4071c612 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; 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 89% 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 c721d0a0d..782407b8e 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,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; import java.util.Arrays; 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 62% 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 7ed62e302..f9cb72c17 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{ 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 8719e01ba..acf57e7de 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{ private static final 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 63% 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 1da8a1ada..6db991ab9 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 62% 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 25e71d2e8..649e16ffe 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 { @Override 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 63% 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 30f5b4e73..6edabc12e 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 { private static final 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 79% rename from src/main/java/cleancode/minesweeper/tobe/user/UserAction.java rename to src/main/java/cleancode/minesweeper/tobe/minesweeper/user/UserAction.java index 64a19afec..1fb6c2c17 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 { OPEN("셀 열기"), FLAG("깃발 꽂기"), UNKNOWN("알 수 없음"); From e68af35c3503bfea60ba1d8c9cd6de226d3bd227 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Sat, 15 Mar 2025 18:24:19 +0900 Subject: [PATCH 25/30] =?UTF-8?q?feat(=EC=BD=94=EB=93=9C-=EB=8B=A4?= =?UTF-8?q?=EB=93=AC=EA=B8=B0):=20=EA=B8=B0=EB=8A=A5=20=EC=9C=A0=EC=A7=80?= =?UTF-8?q?=20=EB=B3=B4=EC=88=98=20>=20=EB=B2=84=EA=B7=B8=20=EC=9E=A1?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../minesweeper/tobe/minesweeper/board/cell/CellState.java | 1 - .../minesweeper/tobe/minesweeper/board/cell/EmptyCell.java | 2 +- .../minesweeper/tobe/minesweeper/board/cell/LandMineCell.java | 2 +- .../minesweeper/tobe/minesweeper/board/cell/NumberCell.java | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellState.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellState.java index df13a777f..4db64592c 100644 --- a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellState.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellState.java @@ -30,7 +30,6 @@ public boolean isOpened() { return isOpened; } - public boolean isFlagged() { return isFlagged; } diff --git a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/EmptyCell.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/EmptyCell.java index dcdde7c33..e3e6e113e 100644 --- a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/EmptyCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/EmptyCell.java @@ -38,7 +38,7 @@ public void open() { @Override public boolean isChecked() { - return cellState.isChecked(); + return cellState.isOpened(); } @Override diff --git a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCell.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCell.java index a21590217..1a2a0e8ba 100644 --- a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCell.java @@ -37,7 +37,7 @@ public void open() { @Override public boolean isChecked() { - return cellState.isChecked(); + return cellState.isFlagged(); } @Override diff --git a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/NumberCell.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/NumberCell.java index 4003ab44d..7f2d461df 100644 --- a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/NumberCell.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/NumberCell.java @@ -43,7 +43,7 @@ public void open() { @Override public boolean isChecked() { - return cellState.isChecked(); + return cellState.isOpened(); } @Override From a11cfc006af18522b335dfd6ecb983329edcef42 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Sat, 15 Mar 2025 22:42:09 +0900 Subject: [PATCH 26/30] =?UTF-8?q?feat(=EC=BD=94=EB=93=9C-=EB=8B=A4?= =?UTF-8?q?=EB=93=AC=EA=B8=B0):=20=EC=95=8C=EA=B3=A0=EB=A6=AC=EC=A6=98=20?= =?UTF-8?q?=EA=B5=90=EC=B2=B4=ED=95=98=EA=B8=B0=20>=20=EA=B8=B0=EC=A1=B4?= =?UTF-8?q?=EC=9D=98=20backtracking=20=EB=B0=A9=EC=8B=9D=20>=20dfs(stack?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9)=20=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tobe/minesweeper/board/GameBoard.java | 27 ++++++++++++++----- .../minesweeper/board/cell/CellState.java | 4 --- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java index fc5a238e6..bbb6409a6 100644 --- a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java @@ -12,6 +12,8 @@ import cleancode.minesweeper.tobe.minesweeper.board.positoion.RelativePosition; import java.util.List; +import java.util.Stack; + public class GameBoard { private final Cell[][] board; @@ -150,21 +152,34 @@ private void updateCellAt(CellPosition position, Cell cell) { } private void openSurroundedCells(CellPosition cellPosition) { - if (isOpenedCell(cellPosition)) { + Stack stack = new Stack<>(); + stack.push(cellPosition); + + while(!stack.isEmpty()) { + openAndPushCellAt(stack); + } + } + + private void openAndPushCellAt(Stack stack) { + CellPosition currentCellPosition = stack.pop(); + + if (isOpenedCell(currentCellPosition)) { return; } - if (isLandMineCellAt(cellPosition)) { + if (isLandMineCellAt(currentCellPosition)) { return; } - openOneCellAt(cellPosition); + openOneCellAt(currentCellPosition); - if (doesCellHaveLandMineCount(cellPosition)) { + if (doesCellHaveLandMineCount(currentCellPosition)) { return; } - List surroundedPositions = calculateSurroundedPositions(cellPosition, getRowSize(), getColSize()); - surroundedPositions.forEach(this::openSurroundedCells); + List surroundedPositions = calculateSurroundedPositions(currentCellPosition, getRowSize(), getColSize()); + for (CellPosition surroundedPosition : surroundedPositions) { + stack.push(surroundedPosition); + } } private void openOneCellAt(CellPosition cellPosition) { diff --git a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellState.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellState.java index 4db64592c..b0c571269 100644 --- a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellState.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellState.java @@ -22,10 +22,6 @@ public void open() { this.isOpened = true; } - public boolean isChecked() { - return isFlagged || isOpened; - } - public boolean isOpened() { return isOpened; } From fbd6c010bd5b4e081f573a4080f17bcfaa77a753 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Sun, 16 Mar 2025 00:31:13 +0900 Subject: [PATCH 27/30] =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=8B=A4=EB=93=AC?= =?UTF-8?q?=EA=B8=B0=20>=20IDE=20=EB=8F=84=EC=9B=80=20=EB=B0=9B=EA=B8=B0?= =?UTF-8?q?=20>=20SonarCube=20>=20Stack=EC=9D=84=20Deque=EB=A1=9C=20?= =?UTF-8?q?=EA=B5=90=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tobe/minesweeper/board/GameBoard.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java index bbb6409a6..b28528825 100644 --- a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java @@ -11,6 +11,8 @@ import cleancode.minesweeper.tobe.minesweeper.board.positoion.CellPositions; import cleancode.minesweeper.tobe.minesweeper.board.positoion.RelativePosition; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.List; import java.util.Stack; @@ -152,15 +154,15 @@ private void updateCellAt(CellPosition position, Cell cell) { } private void openSurroundedCells(CellPosition cellPosition) { - Stack stack = new Stack<>(); - stack.push(cellPosition); + Deque deque = new ArrayDeque<>(); + deque.push(cellPosition); - while(!stack.isEmpty()) { - openAndPushCellAt(stack); + while (!deque.isEmpty()) { + openAndPushCellAt(deque); } } - private void openAndPushCellAt(Stack stack) { + private void openAndPushCellAt(Deque stack) { CellPosition currentCellPosition = stack.pop(); if (isOpenedCell(currentCellPosition)) { From 267b9fc47a45fa1740e8371e831f6e8251e38b56 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Tue, 18 Mar 2025 22:37:44 +0900 Subject: [PATCH 28/30] fix(Minesweeper): ConsoleInputHandler, CellSnapshot row, col index error bug fix --- .../tobe/minesweeper/Minesweeper.java | 2 +- .../tobe/minesweeper/board/GameBoard.java | 53 +++++++++++-------- .../minesweeper/board/cell/CellSnapshot.java | 2 +- .../minesweeper/io/ConsoleInputHandler.java | 2 +- 4 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/main/java/cleancode/minesweeper/tobe/minesweeper/Minesweeper.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/Minesweeper.java index 939839d15..7a85dc033 100644 --- a/src/main/java/cleancode/minesweeper/tobe/minesweeper/Minesweeper.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/Minesweeper.java @@ -39,7 +39,7 @@ public void initialize() { public void run() { outputHandler.showGameStartComments(); - while (gameBoard.inInProgress()) { + while (gameBoard.isInProgress()) { try { outputHandler.showBoard(gameBoard); diff --git a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java index b28528825..aba30a265 100644 --- a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/GameBoard.java @@ -31,8 +31,6 @@ public GameBoard(GameLevel gameLevel) { initializeGameStatus(); } - // public - // 상태 변경 public void initializeGame() { initializeGameStatus(); CellPositions cellPositions = CellPositions.from(board); @@ -53,7 +51,8 @@ public void openAt(CellPosition cellPosition) { return; } - openSurroundedCells(cellPosition); +// openSurroundedCells(cellPosition); + openSurroundedCells2(cellPosition); checkIfGameIsOver(); } @@ -64,7 +63,6 @@ public void flagAt(CellPosition cellPosition) { checkIfGameIsOver(); } - // 판별 public boolean isInvalidCellPosition(CellPosition cellPosition) { int rowSize = getRowSize(); int colSize = getColSize(); @@ -73,12 +71,18 @@ public boolean isInvalidCellPosition(CellPosition cellPosition) { || cellPosition.isColIndexMoreThanOrEqual(colSize); } - public boolean inInProgress() { + 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(); @@ -92,17 +96,6 @@ public int getColSize() { return board[0].length; } - public boolean isWinStatus() { - return gameStatus == GameStatus.WIN; - } - - public boolean isLoseStatus() { - return gameStatus == GameStatus.LOSE; - } - - - // private - private void initializeGameStatus() { gameStatus = GameStatus.IN_PROGRESS; } @@ -154,6 +147,24 @@ private void updateCellAt(CellPosition position, Cell cell) { } private void openSurroundedCells(CellPosition cellPosition) { + if (isOpenedCell(cellPosition)) { + return; + } + if (isLandMineCellAt(cellPosition)) { + return; + } + + openOneCellAt(cellPosition); + + if (doesCellHaveLandMineCount(cellPosition)) { + return; + } + + List surroundedPositions = calculateSurroundedPositions(cellPosition, getRowSize(), getColSize()); + surroundedPositions.forEach(this::openSurroundedCells); + } + + private void openSurroundedCells2(CellPosition cellPosition) { Deque deque = new ArrayDeque<>(); deque.push(cellPosition); @@ -162,9 +173,8 @@ private void openSurroundedCells(CellPosition cellPosition) { } } - private void openAndPushCellAt(Deque stack) { - CellPosition currentCellPosition = stack.pop(); - + private void openAndPushCellAt(Deque deque) { + CellPosition currentCellPosition = deque.pop(); if (isOpenedCell(currentCellPosition)) { return; } @@ -180,7 +190,7 @@ private void openAndPushCellAt(Deque stack) { List surroundedPositions = calculateSurroundedPositions(currentCellPosition, getRowSize(), getColSize()); for (CellPosition surroundedPosition : surroundedPositions) { - stack.push(surroundedPosition); + deque.push(surroundedPosition); } } @@ -226,4 +236,5 @@ private void changeGameStatusToLose() { private Cell findCell(CellPosition cellPosition) { return board[cellPosition.getRowIndex()][cellPosition.getColIndex()]; } + } diff --git a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellSnapshot.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellSnapshot.java index 45a59d2fc..44ae78747 100644 --- a/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellSnapshot.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellSnapshot.java @@ -32,7 +32,7 @@ public static CellSnapshot ofNumber(int nearbyLandMineCount) { } public static CellSnapshot ofUnchecked() { - return new CellSnapshot(CellSnapshotStatus.UNCHECKED, 5); + return new CellSnapshot(CellSnapshotStatus.UNCHECKED, 0); } public boolean isSameStatus(CellSnapshotStatus cellSnapshotStatus) { diff --git a/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/ConsoleInputHandler.java b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/ConsoleInputHandler.java index bb4a5fa7b..0a8df890a 100644 --- a/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/ConsoleInputHandler.java +++ b/src/main/java/cleancode/minesweeper/tobe/minesweeper/io/ConsoleInputHandler.java @@ -26,7 +26,7 @@ public UserAction getUserActionFromUser() { public CellPosition getCellPositionFromUser() { String userInput = SCANNER.nextLine(); - int rowIndex = boardIndexConverter.getSelectedColIndex(userInput); + int rowIndex = boardIndexConverter.getSelectedRowIndex(userInput); int colIndex = boardIndexConverter.getSelectedColIndex(userInput); return CellPosition.of(rowIndex, colIndex); } From 26512227adb7733fea0869ce776d769753f50567 Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Tue, 18 Mar 2025 23:58:30 +0900 Subject: [PATCH 29/30] feat(CellTest): CellState, NumberCell, LAndMineCell, EmptyCell test --- .../minesweeper/board/cell/CellStateTest.java | 48 +++++++++++++++++ .../minesweeper/board/cell/EmptyCellTest.java | 47 +++++++++++++++++ .../board/cell/LandMineCellTest.java | 7 +++ .../board/cell/NumberCellTest.java | 51 +++++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellStateTest.java create mode 100644 src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/EmptyCellTest.java create mode 100644 src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCellTest.java create mode 100644 src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/NumberCellTest.java diff --git a/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellStateTest.java b/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellStateTest.java new file mode 100644 index 000000000..f1ce51a72 --- /dev/null +++ b/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/CellStateTest.java @@ -0,0 +1,48 @@ +package cleancode.minesweeper.tobe.minesweeper.board.cell; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class CellStateTest { + + @Test + @DisplayName("셀 상태를 초기화하면 오픈되지 않고, 깃발이 꽂히지 않은 상태로 초기화된다.") + void initialize() { + // given + CellState cellState = CellState.initialize(); + + // when + + // then + assertFalse(cellState.isOpened()); + assertFalse(cellState.isFlagged()); + } + + @Test + @DisplayName("셀 상태를 깃발 꽂은 상태로 바꿀 수 있다.") + void modifyCellStateFlagged() { + // given + CellState cellState = CellState.initialize(); + + // when + cellState.flag(); + + // then + assertTrue(cellState.isFlagged()); + } + + @Test + @DisplayName("셀 상태를 연 상태로 바꿀 수 있다.") + void modifyCellStateOpened() { + // given + CellState cellState = CellState.initialize(); + + // when + cellState.open(); + + // then + assertTrue(cellState.isOpened()); + } +} diff --git a/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/EmptyCellTest.java b/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/EmptyCellTest.java new file mode 100644 index 000000000..ecf19420b --- /dev/null +++ b/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/EmptyCellTest.java @@ -0,0 +1,47 @@ +package cleancode.minesweeper.tobe.minesweeper.board.cell; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class EmptyCellTest { + + @Test + @DisplayName("빈 셀을 열면 셀의 상태가 열린 상태로 변경된다.") + void cellSnapshotIsEmptyWhenCellStateIsOpened() { + // given + EmptyCell emptyCell = new EmptyCell(); + + // when + emptyCell.open(); + + // then + assertThat(emptyCell.getSnapshot()).isEqualTo(CellSnapshot.ofEmpty()); + } + + @Test + @DisplayName("빈 셀에 깃발을 꽂으면 셀의 상태가 깃발이 꽂힌 상태로 변경된다.") + void cellSnapshotIsFlagWhenCellStateIsFlagged() { + // given + EmptyCell emptyCell = new EmptyCell(); + + // when + emptyCell.flag(); + + // then + assertThat(emptyCell.getSnapshot()).isEqualTo(CellSnapshot.ofFlag()); + } + + @Test + @DisplayName("빈 셀을 초기화하면 셀의 상태가 아무것도 표시 안한 상태로 초기화된다.") + void cellSnapshotIsUncheckedWhenJustInitialize() { + // given + EmptyCell emptyCell = new EmptyCell(); + + // when + + // then + assertThat(emptyCell.getSnapshot()).isEqualTo(CellSnapshot.ofUnchecked()); + } +} diff --git a/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCellTest.java b/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCellTest.java new file mode 100644 index 000000000..82ca34a96 --- /dev/null +++ b/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCellTest.java @@ -0,0 +1,7 @@ +package cleancode.minesweeper.tobe.minesweeper.board.cell; + +import static org.junit.jupiter.api.Assertions.*; + +class LandMineCellTest { + +} diff --git a/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/NumberCellTest.java b/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/NumberCellTest.java new file mode 100644 index 000000000..b024c3e21 --- /dev/null +++ b/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/NumberCellTest.java @@ -0,0 +1,51 @@ +package cleancode.minesweeper.tobe.minesweeper.board.cell; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + + +class NumberCellTest { + + @Test + @DisplayName("숫자 셀을 열면 셀의 상태가 주변에 있는 지뢰의 숫자를 표기한 NUMBER 상태로 변경된다.") + void cellSnapshotIsNumberWhenCellStateIsOpened() { + // given + int nearByLandMineCount = 1; + NumberCell numberCell = new NumberCell(nearByLandMineCount); + + // when + numberCell.open(); + + // then + assertThat(numberCell.getSnapshot()).isEqualTo(CellSnapshot.ofNumber(nearByLandMineCount)); + } + + @Test + @DisplayName("숫자 셀에 깃발을 꽂으면 셀의 상태가 깃발이 꽂힌 상태로 변경된다.") + void cellSnapshotIsFlagWhenCellStateIsFlagged() { + // given + int nearByLandMineCount = 1; + NumberCell numberCell = new NumberCell(nearByLandMineCount); + + // when + numberCell.flag(); + + // then + assertThat(numberCell.getSnapshot()).isEqualTo(CellSnapshot.ofFlag()); + } + + @Test + @DisplayName("숫자 셀을 초기화하면 셀의 상태가 아무것도 표시 안한 상태로 초기화된다.") + void cellSnapshotIsUncheckedWhenJustInitialize() { + // given + int nearByLandMineCount = 1; + NumberCell numberCell = new NumberCell(nearByLandMineCount); + + // when + + // then + assertThat(numberCell.getSnapshot()).isEqualTo(CellSnapshot.ofUnchecked()); + } +} From 3beacfd62c29adceda2fb5a27b763cc884d0f45e Mon Sep 17 00:00:00 2001 From: LeeHyungGeol Date: Wed, 19 Mar 2025 00:42:55 +0900 Subject: [PATCH 30/30] feat(Cell-Test): edit ladnMineCell Test > getSnapshot() --- .../board/cell/LandMineCellTest.java | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCellTest.java b/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCellTest.java index 82ca34a96..e246403ef 100644 --- a/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCellTest.java +++ b/src/test/java/cleancode/minesweeper/tobe/minesweeper/board/cell/LandMineCellTest.java @@ -1,7 +1,47 @@ package cleancode.minesweeper.tobe.minesweeper.board.cell; -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; class LandMineCellTest { + @Test + @DisplayName("지뢰 셀을 열면 셀의 상태가 주변에 있는 지뢰 셀 상태로 변경된다.") + void cellSnapshotIsNumberWhenCellStateIsOpened() { + // given + LandMineCell landMineCell = new LandMineCell(); + + // when + landMineCell.open(); + + // then + assertThat(landMineCell.getSnapshot()).isEqualTo(CellSnapshot.ofLandMine()); + } + + @Test + @DisplayName("지뢰 셀에 깃발을 꽂으면 셀의 상태가 깃발이 꽂힌 상태로 변경된다.") + void cellSnapshotIsFlagWhenCellStateIsFlagged() { + // given + LandMineCell landMineCell = new LandMineCell(); + + // when + landMineCell.flag(); + + // then + assertThat(landMineCell.getSnapshot()).isEqualTo(CellSnapshot.ofFlag()); + } + + @Test + @DisplayName("지뢰 셀을 초기화하면 셀의 상태가 아무것도 표시 안한 상태로 초기화된다.") + void cellSnapshotIsUncheckedWhenJustInitialize() { + // given + LandMineCell landMineCell = new LandMineCell(); + + // when + + // then + assertThat(landMineCell.getSnapshot()).isEqualTo(CellSnapshot.ofUnchecked()); + } }