코딩 테스트(Coding Test)/백준

[백준] 7682번 : 틱택토 - 자바(Java)

다문다뭉 2024. 11. 6. 18:04

Problem 🔒

문제

https://www.acmicpc.net/problem/7682

 

틱택토 게임은 두 명의 사람이 번갈아가며 말을 놓는 게임이다. 게임판은 3×3 격자판이며, 처음에는 비어 있다. 두 사람은 각각 X 또는 O 말을 번갈아가며 놓는데, 반드시 첫 번째 사람이 X를 놓고 두 번째 사람이 O를 놓는다. 어느 때든지 한 사람의 말이 가로, 세로, 대각선 방향으로 3칸을 잇는 데 성공하면 게임은 즉시 끝난다. 게임판이 가득 차도 게임은 끝난다.

게임판의 상태가 주어지면, 그 상태가 틱택토 게임에서 발생할 수 있는 최종 상태인지를 판별하시오.

입력

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 줄은 9개의 문자를 포함하며, 'X', 'O', '.' 중 하나이다. '.'은 빈칸을 의미하며, 9개의 문자는 게임판에서 제일 윗 줄 왼쪽부터의 순서이다. 입력의 마지막에는 문자열 "end"가 주어진다.

출력

각 테스트 케이스마다 한 줄에 정답을 출력한다. 가능할 경우 "valid", 불가능할 경우 "invalid"를 출력한다.

더보기

예제 입력 1

XXXOO.XXX
XOXOXOXOX
OXOXOXOXO
XXOOOXXOX
XO.OX...X
.XXX.XOOO
X.OO..X..
OOXXXOOXO
end

예제 출력 1

invalid
valid
invalid
valid
valid
invalid
invalid
invalid

Approach ⭕

  1. X말과 O말의 개수 확인
    • 정상적인 게임 판이라면 0≤ (X-O) ≤1을 만족해야한다.
  2. 승리했는지 여부 확인
    1. X와 O중에서 누가 승리했는지 확인한다.(Win 메서드)
      • 가능한 승리 조건을 able 배열에 저장한다.
      • 각 조건을 확인하면서 3칸이 모두 같은 문자인 경우 승리했다고 판단한다.
    2. 승리한 경우, 가능한 게임 상황인지 판별한다.
      • X와 O 둘 다 승리하는 경우는 불가능하다.
      • X가 승리한 경우, X말의 수가 O말의 수보다 많아야 한다.
      • O가 승리한 경우, X말의 수와 O말의 수가 같아야 한다.
  3. 이전 조건들을 통과했다면, 게임판이 꽉 찼는지 확인한다.
    • 승부가 결정되지 않은 상태로, 게임판이 다 찼다면 valid로 처리한다.
  4. 위의 조건들에서 처리되지 않았다면, invalid로 처리한다.

Solution 💡

import java.util.Scanner;

public class 틱택토 {
    static int[][] able = {{0,1,2},{3,4,5},{6,7,8},{0,3,6},{1,4,7},{2,5,8},{0,4,8},{2,4,6}};
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        Scanner sc = new Scanner(System.in);
        boolean B = true;

        while (B){
            String game = sc.next();
            if(game.equals("end")){B=false; break;} // 입력 끝
            int nX=0, nO=0;// X말 개수, O말 개수
            char[] map = new char[9];
            for(int i=0; i<9; i++){
                map[i] = game.charAt(i);
                if(map[i]=='X') nX++;
                if(map[i]=='O') nO++;
            }

            // 1. X말 개수와 O말 개수 차이 확인
            if(nX-nO!=0 && nX-nO!=1) {sb.append("invalid").append("\\n"); continue;}

            // 2. 승리한 사람이 있는지 확인
            boolean Xwin = Win(map, 'X');
            boolean Owin = Win(map, 'O');

            if(Xwin && Owin) {sb.append("invalid").append("\\n"); continue;}
            if(Xwin){ if(nX>nO){sb.append("valid").append("\\n"); continue;} else{sb.append("invalid").append("\\n"); continue;}}
            if(Owin){ if(nX==nO){sb.append("valid").append("\\n"); continue;} else{sb.append("invalid").append("\\n"); continue;}}

            // 3. 1,2단계를 다 통과하고, 모든 말이 다 찼으면 valid
            if(nX+nO==9) {sb.append("valid").append("\\n"); continue;}

            // 4. 그 외 나머지는 invalid
            sb.append("invalid").append("\\n");

        }// while

        System.out.println(sb);
    }

    public static boolean Win(char[] map, char h){
        for(int[] idx : able){
            if(map[idx[0]]==h && map[idx[1]]==h && map[idx[2]]==h){
                return true;
            }
        }
        return false;
    }
}

개선할 점 🔧

  1. 입력 종료 시 “end”가 주어졌을때, while문 안에서 break만 써도 while문이 종료된다.
  2. 승리했는지 여부 확인 후, 가능한 게임 상황인지 확인할 때 중복된 조건을 제거하고, else문을 활용한다면 더욱 구조화된 코드를 작성할 수 있다.
    • X와 O가 동시에 승리하는 경우를 처리했는데 다음 조건에서 한 번 더 확인했다.
    • if문을 남발하여 코드가 산만하고 구조적이지 않았다.
    // 2. 승리한 사람이 있는지 확인
    boolean Xwin = Win(map, 'X');
    boolean Owin = Win(map, 'O');
    if(Xwin && Owin) {sb.append("invalid").append("\\n"); continue;}
    if(Xwin && !Owin && nX>nO) {sb.append("valid").append("\\n"); continue;}
    if(Owin && !Xwin && nX==nO) {sb.append("valid").append("\\n"); continue;}
    if(Owin && !Xwin && nX>nO) {sb.append("invalid").append("\\n"); continue;}

결과