교수님이 시키신 지뢰찾기 추가과제
GPT랑 공부해봤습니다.
코드는 다음과 같습니다. 상당히 깁니다. 하나씩 뜯어보며 개념까지 같이 정리해봅시다.
package week5;
import java.util.Random;
import java.util.Scanner;
public class 지뢰찾기 {
private char[][] board; // 사용자 보드
private boolean[][] mineBoard; // 지뢰 위치 저장
private int rows, cols, mineCount;
private boolean[][] revealed; // 열린 셀 표시
static Scanner input = new Scanner(System.in);
// 🔹 생성자: 게임 보드 초기화
public 지뢰찾기(int rows, int cols, int mineCount) { //지역변수/매개변수 입력받기
this.rows = rows; // 멤버변수(전역)에 지역변수(매개) 값 입력받기
this.cols = cols;
this.mineCount = mineCount;
board = new char[rows][cols];
mineBoard = new boolean[rows][cols];
revealed = new boolean[rows][cols];
// 보드 초기화
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
board[i][j] = '-';
revealed[i][j] = false;
}
}
placeMines(); // 지뢰 배치
calculateNumbers(); // 숫자 계산
}
// 🔹 지뢰 배치
private void placeMines() {
Random rand = new Random();
int placedMines = 0;
while (placedMines < mineCount) {
int r = rand.nextInt(rows);
int c = rand.nextInt(cols);
if (!mineBoard[r][c]) { // 이미 지뢰가 없을 경우 배치
mineBoard[r][c] = true;
placedMines++;
}
}
}
// 🔹 주변 숫자 계산
private void calculateNumbers() {
int[] dr = {-1, -1, -1, 0, 0, 1, 1, 1};
int[] dc = {-1, 0, 1, -1, 1, -1, 0, 1};
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (mineBoard[r][c]) {
board[r][c] = '*'; // 지뢰
continue;
}
int count = 0;
for (int i = 0; i < 8; i++) {
int nr = r + dr[i];
int nc = c + dc[i];
if (isValid(nr, nc) && mineBoard[nr][nc]) {
count++;
}
}
board[r][c] = (count > 0) ? (char) ('0' + count) : ' ';
}
}
}
// 🔹 보드 출력 (지뢰 감추기 가능)
public void printBoard(boolean revealMines) {
System.out.print(" ");
for (int c = 0; c < cols; c++) System.out.print(c + " ");
System.out.println();
for (int r = 0; r < rows; r++) {
System.out.print(r + " ");
for (int c = 0; c < cols; c++) {
if (revealMines || revealed[r][c]) {
System.out.print(board[r][c] + " ");
} else {
System.out.print("- ");
}
}
System.out.println();
}
}
// 🔹 셀 열기
public boolean openCell(int row, int col) {
if (!isValid(row, col) || revealed[row][col]) return false;
revealed[row][col] = true;
if (mineBoard[row][col]) {
System.out.println("💥 지뢰를 밟았습니다! 게임 오버!");
return true; // 게임 종료
}
// 0인 경우, 주변도 자동으로 열기
if (board[row][col] == ' ') {
int[] dr = {-1, -1, -1, 0, 0, 1, 1, 1};
int[] dc = {-1, 0, 1, -1, 1, -1, 0, 1};
for (int i = 0; i < 8; i++) {
openCell(row + dr[i], col + dc[i]);
}
}
return false;
}
// 🔹 게임 승리 여부 확인
public boolean isGameWon() {
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (!mineBoard[r][c] && !revealed[r][c]) {
return false; // 아직 안 열린 셀 있음
}
}
}
return true;
}
// 🔹 좌표가 유효한지 확인
private boolean isValid(int r, int c) {
return r >= 0 && r < rows && c >= 0 && c < cols;
}
// 🔹 게임 실행 메서드
public void play() {
Scanner scanner = new Scanner(System.in);
boolean gameOver = false;
while (!gameOver) {
printBoard(false);
System.out.print("행 열 입력 (예: 2 3): ");
int row = scanner.nextInt();
int col = scanner.nextInt();
gameOver = openCell(row, col);
if (isGameWon()) {
System.out.println("🎉 축하합니다! 모든 칸을 열었습니다!");
break;
}
}
printBoard(true);
scanner.close();
}
public static void main(String[] args) {
지뢰찾기 game = new 지뢰찾기(5, 5, 5);
game.play();
}
}
코드를 하나하나 뜯어보자!
private char[][] board; // 사용자 보드
private boolean[][] mineBoard; // 지뢰 위치 저장
private int rows, cols, mineCount;
private boolean[][] revealed; // 열린 셀 표시
static Scanner input = new Scanner(System.in);
1) 접근제어자
public / protected / default/ privated
접근 보안성
(낮음) - public < protected < default < private- (높음)
|
public
|
다른클래스에서도 접근 가능/ 어디에서나 접근 가능
|
|
protected
|
동일한 패키지에 속하는 클래스에서 접근 가능
|
|
default
|
접근 제어자를 사용하지 않으면 자동으로 default 속성
동일한 패키지에 속하는 클래스에서 접근 가능하나, 자식 클래스에서 상속 불가능
|
|
private
|
다른 클래스에서 접근 불가.
해당 멤버가 선언된 클래스 안에서만 사용 가능하고, 상속이 불가능
|
// 🔹 생성자: 게임 보드 초기화
public 지뢰찾기(int rows, int cols, int mineCount) { //지역변수/매개변수 입력받기
this.rows = rows; // 멤버변수(전역)에 지역변수(매개) 값 입력받기
this.cols = cols;
this.mineCount = mineCount;
board = new char[rows][cols];
mineBoard = new boolean[rows][cols];
revealed = new boolean[rows][cols];
// 보드 초기화
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
board[i][j] = '-';
revealed[i][j] = false;
}
}
placeMines(); // 지뢰 배치
calculateNumbers(); // 숫자 계산
}
🔥 생성자 (Constructor)란?
생성자(Constructor) : 클래스의 인스턴스(객체)를 생성할 때 자동으로 호출되는 특수한 메서드
객체가 만들어질 때 초기화 작업을 담당한다.
====================================
📌 생성자의 특징
1️⃣ 클래스 이름과 동일해야 함
2️⃣ 리턴 타입이 없음 (void도 안 씀!)
3️⃣ 객체가 생성될 때 한 번만 호출됨
4️⃣ 객체의 초기값을 설정하는 역할
=====================================
지뢰찾기에서 생성자의 역할:
✅ 지뢰찾기 game = new 지뢰찾기(5, 5, 5);
➡ 객체가 생성될 때 자동으로 호출되면서 게임 보드의 크기와 지뢰 개수를 설정하는 역할을 한다.
멤버변수를 초기화하는 역할을 가진다.
📌 생성자의 종류
1️⃣ 기본 생성자 (Default Constructor)
2️⃣ 오버로딩된 생성자 (여러 개의 생성자)
➡ 같은 이름의 생성자를 여러 개 정의 가능 (매개변수 개수/타입 다르게)
➡ 매개변수에 따라 호출되는 생성자가 다름
💡 결론: 생성자는 왜 필요할까?
1️⃣ 객체가 생성될 때 자동 실행
2️⃣ 초기값을 설정해 줌 (멤버 변수 초기화)
3️⃣ 객체마다 다른 값을 가질 수 있음
4️⃣ 가독성이 좋아지고, 실수를 줄일 수 있음
생성자가 없으면 객체 생성 후 따로 초기화를 해줘야 하는데,
생성자가 있으면 객체가 만들어지면서 자동으로 초기화되니까 편리함! 😃🔥
(1) this
this는 객체 자신을 의미하며 super는 상속관계에서 부모클래스의 객체를 가리킨다.
this.x =x; 문장에서 x는 매개변수로 받은 값을 의미하고,
this.x는 Robot클래스 멤버변수 x를 의미함.
this = 객체 자신
private void placeMines() {
Random rand = new Random();
int placedMines = 0;
while (placedMines < mineCount) {
int r = rand.nextInt(rows);
int c = rand.nextInt(cols);
if (!mineBoard[r][c]) { // 이미 지뢰가 없을 경우 배치
mineBoard[r][c] = true;
placedMines++;
}
}
}
지뢰배치부분
new 연산자를 통해 Random 객체를 하나 생성했다.
배치된 지뢰 수 < 지뢰의 개수(입력받은거)
지뢰판의 행/열보다 작은 0 이상의 정수를 저장한다. (랜덤위치 지정)
해당 index의 bool 타입을 false => true로 변경한다.
지뢰를 배치하였으므로, 배치된지뢰 수를 1씩 증가시킨다.
private void calculateNumbers() {
int[] dr = {-1, -1, -1, 0, 0, 1, 1, 1};
int[] dc = {-1, 0, 1, -1, 1, -1, 0, 1};
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (mineBoard[r][c]) {
board[r][c] = '*'; // 지뢰
continue;
}
int count = 0;
for (int i = 0; i < 8; i++) {
int nr = r + dr[i];
int nc = c + dc[i];
if (isValid(nr, nc) && mineBoard[nr][nc]) {
count++;
}
}
board[r][c] = (count > 0) ? (char) ('0' + count) : ' ';
}
}
}
주변 지뢰 수 계산하기
이 메서드는 지뢰찾기 게임에서 각 칸의 숫자를 계산하는 역할을 해!
즉, 각 칸에 대해 "주변 8칸에 지뢰가 몇 개 있는지" 계산해서 숫자를 채워 넣는 함수야.
📌 코드 분석 (전체 흐름)
// 🔹 주변 숫자 계산
private void calculateNumbers() {
✅ calculateNumbers() 메서드는 private이므로, 클래스 내부에서만 호출 가능
✅ 이 메서드는 보드를 한 칸씩 돌면서, 지뢰 주변 숫자를 계산하는 역할을 함
📌 dr과 dc 배열: 주변 8방향 탐색
int[] dr = {-1, -1, -1, 0, 0, 1, 1, 1}; // 행 이동
int[] dc = {-1, 0, 1, -1, 1, -1, 0, 1}; // 열 이동
✅ dr과 dc 배열은 8방향을 탐색하기 위한 배열이야.
✅ (위, 아래, 좌, 우, 대각선) 방향을 한 번에 탐색할 수 있게 함.
| index | dr | dc | 방향 |
| 0 | -1 | -1 | ↖ (왼쪽 위) |
| 1 | -1 | 0 | ↑ (위) |
| 2 | -1 | 1 | ↗ (오른쪽 위) |
| 3 | 0 | -1 | ← (왼쪽) |
| 4 | 0 | 1 | → (오른쪽) |
| 5 | 1 | -1 | ↙ (왼쪽 아래) |
| 6 | 1 | 0 | ↓ (아래) |
| 7 | 1 | 1 | ↘ (오른쪽 아래) |
✅ 예를 들어 (r, c)가 (2,3)이라면?
- dr[0] = -1, dc[0] = -1 ➝ (1,2) (↖ 왼쪽 위)
- dr[1] = -1, dc[1] = 0 ➝ (1,3) (↑ 위)
- dr[2] = -1, dc[2] = 1 ➝ (1,4) (↗ 오른쪽 위)
- … 이렇게 8개 방향을 탐색할 수 있음.
📌 보드 순회 (for 루프)
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
✅ 이중 for 문으로 게임 보드를 한 칸씩 검사
✅ r은 행(row), c는 열(column)
✅ rows와 cols는 보드 크기
즉, 보드의 (0,0)부터 (rows-1, cols-1)까지 모든 칸을 검사함.
📌 if 문: 현재 칸이 지뢰인지 확인
if (mineBoard[r][c]) {
board[r][c] = '*'; // 지뢰
continue;
}
✅ mineBoard[r][c] == true라면? → 지뢰가 있는 칸
✅ board[r][c]에 '*'을 저장하고 다음 칸으로 이동 (continue;)
✅ 지뢰가 있는 칸은 숫자를 계산할 필요 없음!
📌 count 변수 초기화 (지뢰 개수 세기)
int count = 0;
✅ count는 현재 칸 주위에 있는 지뢰 개수를 저장하는 변수
✅ 초기값 0으로 설정하고, 주변 8칸을 검사하면서 값을 증가시킴
📌 for 문: 8방향 탐색
for (int i = 0; i < 8; i++) {
int nr = r + dr[i];
int nc = c + dc[i];
✅ i는 0부터 7까지 반복 (8방향 탐색)
✅ nr = r + dr[i], nc = c + dc[i]
→ nr, nc는 현재 칸 (r, c)에서 i번째 방향으로 이동한 좌표
예를 들어, (r, c) = (2,3)일 때 i=0이면:
- dr[0] = -1, dc[0] = -1
- nr = 2 + (-1) = 1
- nc = 3 + (-1) = 2
- 즉, (1,2) 좌표를 검사!
📌 isValid(nr, nc): 유효한 좌표인지 체크
if (isValid(nr, nc) && mineBoard[nr][nc]) {
count++;
}
✅ isValid(nr, nc)
→ nr과 nc가 보드 안에 있는 좌표인지 확인하는 함수
→ (예: nr이 음수이거나 cols보다 크면 안 됨)
✅ mineBoard[nr][nc] == true라면?
→ 그 위치에 지뢰가 있으면 count++ 증가
즉, 현재 칸 (r,c) 주변 8칸 중 지뢰가 몇 개 있는지 세는 부분!
📌 지뢰 개수에 따라 숫자 채우기
board[r][c] = (count > 0) ? (char) ('0' + count) : ' ';
✅ count > 0이면 → char로 숫자로 변환
✅ ('0' + count)는 숫자를 문자로 변환하는 트릭
- 예를 들어, count = 3이면 '0' + 3 → '3'
✅ 지뢰 개수가 0이면 공백 ' '을 넣음
📌 최종 정리 (전체 흐름)
1️⃣ 보드 전체를 탐색 (for 루프)
2️⃣ 현재 칸이 지뢰(*)라면 건너뛰기
3️⃣ 8방향을 탐색하며 지뢰 개수 세기
4️⃣ 개수에 따라 숫자(1~8) 또는 빈 칸(' ') 채우기
💡 예제 실행
// 🔹 지뢰 배치 예시 (`*`는 지뢰)
mineBoard =
[
[false, false, false, false, false],
[false, true, false, false, false],
[false, false, false, false, false],
[false, false, true, false, false],
[false, false, false, false, false]
];
// 🔹 계산된 보드 (`calculateNumbers()` 실행 후)
board =
[
['1', '1', '1', ' ', ' '],
['1', '*', '1', ' ', ' '],
['1', '1', '2', '1', '1'],
[' ', '1', '*', '1', ' '],
[' ', '1', '1', '1', ' ']
];
✅ (1,1)과 (3,2)에 지뢰가 있음 (*)
✅ calculateNumbers()가 실행되면 주변 숫자가 자동으로 계산됨
📌 결론
이 메서드는 보드의 모든 칸을 돌면서 주변 8칸의 지뢰 개수를 세고, 그에 맞는 숫자를 채워주는 역할을 한다.
// 🔹 보드 출력 (지뢰 감추기 가능)
public void printBoard(boolean revealMines) {
System.out.print(" ");
for (int c = 0; c < cols; c++) System.out.print(c + " ");
System.out.println();
for (int r = 0; r < rows; r++) {
System.out.print(r + " ");
for (int c = 0; c < cols; c++) {
if (revealMines || revealed[r][c]) {
System.out.print(board[r][c] + " ");
} else {
System.out.print("- ");
}
}
System.out.println();
}
}
🔥 printBoard(boolean revealMines) 메서드 완전 해부! 🔥
이 메서드는 지뢰찾기 보드를 출력하는 역할을 해! 🕹️
특히 revealMines 값을 이용해서 지뢰를 숨길지, 보여줄지를 결정할 수 있어.
📌 전체 코드 분석
public void printBoard(boolean revealMines) {
✅ printBoard()는 보드를 출력하는 메서드
✅ 매개변수 revealMines가 true면 → 모든 칸을 보여줌 (디버깅 모드)
✅ revealMines가 false면 → 아직 오픈되지 않은 칸을 '-'로 표시
📌 보드의 열 번호 출력
System.out.print(" ");
for (int c = 0; c < cols; c++) System.out.print(c + " ");
System.out.println();
✅ 첫 번째 줄에 열(column) 번호를 출력하는 부분
✅ cols(보드의 가로 크기)만큼 반복해서 각 열의 번호를 출력
📌 예시 (5x5 보드)
0 1 2 3 4
👉 출력된 보드를 보기 쉽게 하기 위해, 앞에 " "(공백 두 칸)를 추가함.
📌 보드 내용 출력 (for 루프)
for (int r = 0; r < rows; r++) {
✅ rows만큼 반복 → 보드의 각 행을 출력
📌 행 번호 출력
System.out.print(r + " ");
✅ 각 행의 맨 앞에 행 번호를 출력
📌 예시 (5x5 보드)
0 1 2 3 4
0 - - - - -
1 - - - - -
2 - - - - -
3 - - - - -
4 - - - - -
🔹 왼쪽에 0 1 2 3 4로 행 번호 표시
📌 실제 보드 내용 출력
for (int c = 0; c < cols; c++) {
if (revealMines || revealed[r][c]) {
System.out.print(board[r][c] + " ");
} else {
System.out.print("- ");
}
}
✅ cols만큼 반복 → 보드의 각 열을 출력
✅ revealMines == true면? → 모든 칸을 보여줌 (디버깅 모드)
✅ revealMines == false면?
- revealed[r][c] == true → 오픈된 칸은 보여줌
- revealed[r][c] == false → '-' 출력 (아직 오픈되지 않음)
📌 줄 바꿈 (행 출력이 끝나면 개행)
System.out.println();
✅ 한 행을 출력한 후, 줄 바꿈해서 다음 행을 출력
📌 예제 실행
1️⃣ 게임 시작 시 (revealMines = false)
📌 revealed 배열이 모두 false (아직 열지 않은 상태)
0 1 2 3 4
0 - - - - -
1 - - - - -
2 - - - - -
3 - - - - -
4 - - - - -
2️⃣ 일부 칸 오픈 (revealMines = false)
📌 revealed[2][2] = true, revealed[3][3] = true 일 때
0 1 2 3 4
0 - - - - -
1 - - - - -
2 - - 2 - -
3 - - - 1 -
4 - - - - -
✅ (2,2)와 (3,3)에 숫자가 표시됨!
3️⃣ 디버깅 모드 (revealMines = true)
📌 모든 칸을 보여줌 (지뢰도 표시됨)
📌 board[r][c] 값을 그대로 출력
0 1 2 3 4
0 1 1 1 - -
1 1 * 1 - -
2 1 2 2 1 1
3 - 1 * 1 -
4 - 1 1 1 -
✅ *는 지뢰가 있는 칸
✅ 2,1 등의 숫자는 주변 지뢰 개수를 나타냄
✅ '-'는 여전히 빈 칸
📌 결론 (한 줄 요약)
printBoard()는 보드를 예쁘게 출력하는 함수야!
- revealMines == true → 모든 칸을 보여줌 (디버깅 모드)
- revealMines == false → 오픈된 칸만 보여주고, 나머지는 '-'로 숨김
이해됐어? 더 궁금한 거 있으면 질문해~ 🚀😃
// 🔹 셀 열기
public boolean openCell(int row, int col) {
if (!isValid(row, col) || revealed[row][col]) return false;
revealed[row][col] = true; //사용자판 트루
if (mineBoard[row][col]) { // 지뢰판에 트루 = 지뢰 있다면..
System.out.println("💥 지뢰를 밟았습니다! 게임 오버!");
return true; // 게임 종료
}
// 0인 경우, 주변도 자동으로 열기
if (board[row][col] == ' ') {
int[] dr = {-1, -1, -1, 0, 0, 1, 1, 1};
int[] dc = {-1, 0, 1, -1, 1, -1, 0, 1};
for (int i = 0; i < 8; i++) {
openCell(row + dr[i], col + dc[i]);
}
}
return false;
}
사용자판 / 지뢰판 비교해서한다. 이후, 재귀방식으로, 한번에 다깡)
// 🔹 게임 승리 여부 확인
public boolean isGameWon() {
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (!mineBoard[r][c] && !revealed[r][c]) {
return false; // 아직 안 열린 셀 있음
}
}
}
return true;
}
// 🔹 좌표가 유효한지 확인
private boolean isValid(int r, int c) {
return r >= 0 && r < rows && c >= 0 && c < cols;
}
// 🔹 게임 실행 메서드
public void play() {
Scanner scanner = new Scanner(System.in);
boolean gameOver = false;
while (!gameOver) {
printBoard(false);
System.out.print("행 열 입력 (예: 2 3): ");
int row = scanner.nextInt();
int col = scanner.nextInt();
gameOver = openCell(row, col);
if (isGameWon()) {
System.out.println("🎉 축하합니다! 모든 칸을 열었습니다!");
break;
}
}
printBoard(true);
scanner.close();
}
public static void main(String[] args) {
지뢰찾기 game = new 지뢰찾기(5, 5, 5);
game.play();
}
}'컴퓨터공학과 > Java 1 & 2' 카테고리의 다른 글
| WEEK7 . chap05-class (0) | 2025.04.15 |
|---|---|
| Week6 : JAVA프로그래밍및실습 (0) | 2025.04.11 |
| JAVA 프로그래밍 및 실습 추가학습 (0) | 2025.04.03 |
| JAVA 프로그래밍 및 실습 (0) | 2025.04.01 |
| [#실습2 JAVA프로그래밍및실습 I ] (0) | 2025.03.18 |