JAVA 지뢰찾기

2025. 4. 4. 17:51·컴퓨터공학과/Java 1 & 2

교수님이 시키신 지뢰찾기 추가과제

 

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
'컴퓨터공학과/Java 1 & 2' 카테고리의 다른 글
  • WEEK7 . chap05-class
  • Week6 : JAVA프로그래밍및실습
  • JAVA 프로그래밍 및 실습 추가학습
  • JAVA 프로그래밍 및 실습
sihyes
sihyes
24학번 컴퓨터공학과
  • sihyes
    시혜적으로개발
    sihyes
  • 글쓰기 관리
  • 전체
    오늘
    어제
    • 분류 전체보기 (114)
      • 단순 설정 (10)
      • 백엔드 공부(BE, AWS) (8)
        • 로그인&회원가입 (3)
        • 파일업로드&GPT (2)
      • 컴퓨터공학과 (51)
        • 운영체제 (0)
        • Artificial Intelligence (0)
        • Java 1 & 2 (23)
        • 컴퓨터네트워크 (3)
        • 모앱JavaScript (0)
        • Data structures (9)
        • 소프트웨어공학 (5)
        • 오픈SW플랫폼 제출용 (5)
        • Python - 문해프 (1)
      • 개인 프로젝트 (2)
        • 알바솔로몬 (1)
        • PLACO 프로젝트 (0)
      • 도서 공부(정리) (20)
        • 알고리즘 코딩 테스트 자바 편 (1)
        • SQL첫걸음 (8)
        • 코딩 자율학습 스프링 부트 3 자바 백엔드 개발 .. (6)
        • Do it! 지옥에서 온 문서 관리자 깃&깃허브 .. (5)
      • 개인공부정리페이지 (12)
        • 백준 & 프로그래머스 (3)
  • 블로그 메뉴

    • 홈
    • 태그
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    ㅇ
  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.4
sihyes
JAVA 지뢰찾기
상단으로

티스토리툴바