혼자하는 틱택토 자바

바그다드·2023년 3월 13일
0

알고리즘

목록 보기
5/14

문제 설명

틱택토는 두 사람이 하는 게임으로 처음에 3x3의 빈칸으로 이루어진 게임판에 선공이 "O", 후공이 "X"를 번갈아가면서 빈칸에 표시하는 게임입니다. 가로, 세로, 대각선으로 3개가 같은 표시가 만들어지면 같은 표시를 만든 사람이 승리하고 게임이 종료되며 9칸이 모두 차서 더 이상 표시를 할 수 없는 경우에는 무승부로 게임이 종료됩니다.

할 일이 없어 한가한 머쓱이는 두 사람이 하는 게임인 틱택토를 다음과 같이 혼자서 하려고 합니다.

혼자서 선공과 후공을 둘 다 맡는다.
틱택토 게임을 시작한 후 "O"와 "X"를 혼자서 번갈아 가면서 표시를 하면서 진행한다.
틱택토는 단순한 규칙으로 게임이 금방 끝나기에 머쓱이는 한 게임이 종료되면 다시 3x3 빈칸을 그린 뒤 다시 게임을 반복했습니다. 그렇게 틱택토 수 십 판을 했더니 머쓱이는 게임 도중에 다음과 같이 규칙을 어기는 실수를 했을 수도 있습니다.

"O"를 표시할 차례인데 "X"를 표시하거나 반대로 "X"를 표시할 차례인데 "O"를 표시한다.
선공이나 후공이 승리해서 게임이 종료되었음에도 그 게임을 진행한다.
게임 도중 게임판을 본 어느 순간 머쓱이는 본인이 실수를 했는지 의문이 생겼습니다. 혼자서 틱택토를 했기에 게임하는 과정을 지켜본 사람이 없어 이를 알 수는 없습니다. 그러나 게임판만 봤을 때 실제로 틱택토 규칙을 지켜서 진행했을 때 나올 수 있는 상황인지는 판단할 수 있을 것 같고 문제가 없다면 게임을 이어서 하려고 합니다.

머쓱이가 혼자서 게임을 진행하다 의문이 생긴 틱택토 게임판의 정보를 담고 있는 문자열 배열 board가 매개변수로 주어질 때, 이 게임판이 규칙을 지켜서 틱택토를 진행했을 때 나올 수 있는 게임 상황이면 1을 아니라면 0을 return 하는 solution 함수를 작성해 주세요.

제한사항
board의 길이 = board[i]의 길이 = 3
board의 원소는 모두 "O", "X", "."으로만 이루어져 있습니다.
board[i][j]는 i + 1행 j + 1열에 해당하는 칸의 상태를 나타냅니다.
"."은 빈칸을, "O"와 "X"는 해당 문자로 칸이 표시되어 있다는 의미입니다.
입출력 예
board result
["O.X", ".O.", "..X"] 1
["OOO", "...", "XXX"] 0
["...", ".X.", "..."] 0
["...", "...", "..."] 1
입출력 예 설명
입출력 예 #1

예제 1번의 게임판은 다음과 같습니다.

O.X
.O.
..X
선공 후공이 번갈아가면서 다음과 같이 놓았을 때 이러한 게임판이 나올 수 있습니다.

1행 1열 → 1행 3열 → 2행 2열 → 3행 3열
1행 1열 → 3행 3열 → 2행 2열 → 1행 3열
2행 2열 → 1행 3열 → 1행 1열 → 3행 3열
2행 2열 → 3행 3열 → 1행 1열 → 1행 3열
물론 위와 다르게 머쓱이가 2행 2열에 O, 3행 3열에 X, 1행 3열에 X, 1행 1열에 O 순서로 표시를 해서 실수를 했을 가능성도 있지만 "실수를 했을 가능성이 있는가"를 묻는 게 아닌 "이 게임판이 규칙을 지켜서 진행한 틱택토에서 나올 수 있는 상황인가"를 묻는 문제라는 것에 유의해주세요. 따라서 1을 return 합니다.

입출력 예 #2

예제 2번의 게임판은 다음과 같습니다.

OOO
...
XXX
규칙을 지켜서 진행한 틱택토라면 선공과 후공이 번갈아가면서 각각 1행, 3행 중 두 칸씩에 표시를 한 뒤 5번째 차례에 선공이 1행에 가로로 3개의 O를 완성했을 때 종료되므로 적어도 머쓱이가 게임이 종료된 후에도 계속 진행하는 실수를 했다는 것을 추론해 볼 수 있고, 정상적인 틱택토에서는 이러한 상황이 나올 수 없습니다. 따라서 0을 return 합니다.

입출력 예 #3

예제 3번은 2행 2열에만 X가 표시가 되어있습니다. 선공 O 표시가 없이 X만 있으므로 머쓱이가 O를 표시해야 할 때 X를 표시하는 실수를 했다는 것을 추론해 볼 수 있고, 규칙을 지켜서 진행했을 때는 이러한 상황이 나올 수 없습니다. 따라서 0을 return 합니다.
입출력 예 #4

예제 4번은 빈 3x3 게임판입니다. 선공이 아직 빈칸에 표시하기 전에 이러한 상황이 나올 수 있습니다. 따라서 1을 return 합니다.

리뷰

경우의 수를 생각해보았다.
오답의 경우를 생각하는 것보다, 정답의 경우의 수를 생각해보는게 
더 나을거 같아서 정답이 될 경우의 수를 기준으로 문제를 풀어보았다.

o가 x보다 하나만 많고 x가 정답이 아닌 경우 = 1,
o와 x가 같은 경우,
	같을 때 o가 정답이 아닐경우 = 1,
그 외는 0

이렇게 경우의 수를 짜봤는데 14번, 26번, 30번 케이스에서 오답이 나왔다.
그런데 반례가 도저히 떠오르질 않아서 몇 시간 동안 해멨는지 모르겠다.

이리저리 한참을 알아본 결과 결국 내 오타 문제였다ㅜㅜ😂😂😂😂😂

대각선 정답을 확인할 때 1행3열부터 시작해서 
3행1열로 이어지는 대각선에서 x의 값이 몇개인지 누적하는 tmpxr을 tmpxl로 잘못 입력했던 것이다.

변수명 생각해내는게 머리 아파서 생각 나는대로 변수명을 지정했다가 혼자 똥꼬쇼를 한 것이다.

이걸 수정하니 모든 테스트 케이스를 통과해서 무척 기뻤지만 동시에 무척 허무했다.
아무리 생각해봐도 경우의 수가 더 나올 수가 없는데 자꾸 에러가 떠서 참다참다 다른 사람의 풀이를 봤고
결국 거기서도 해답을 찾지 못해서 여기저기 찾아봤던 문제인데 결국 내 오타 문제였다는게 허무했다....
어우 진짜 이것만 몇 시간을 붙잡고 있었던거지...😤😤

이것 때문에 스트레스 받아서 정답률 높은 쉬운 문제 2개로 보상 심리를 채우려 했는데,
너무 쉬워서 그것도 안됐다.

정답 코드


class Solution {
    public int solution(String[] board) {
        int answer = -1;
		
        // o와 x의 총 개수를 누적하기 위한 변수
        int cnto = 0;
        int cntx = 0;
		
        // o와 x에서 정답이 나왔는지 확인하기 위한 변수
        boolean o = false;
        boolean x = false;
		
        // 각 열에서 o와 x의 개수를 누적할 배열
        int[] arro = {0,0,0};
        int[] arr = {0,0,0};
		// 각 대각선에서 o의 개수를 누적할 변수
        int tmpl = 0;
        int tmpr = 0;
        
        // 각 대각선에서 x의 개수를 누적할 변수
        int tmpxl = 0;
        int tmpxr = 0;

        for(int i = 0; i < board.length; i++){
            // 각 행이 정답인지 확인
            if(board[i].equals("XXX")){
                x=true;
            }else if(board[i].equals("OOO")){
                o = true;
            }
            
            // 각 대각선의 o x 개수를 누적
            if(board[i].charAt(i)=='O'){
                tmpl ++;
            }else if(board[i].charAt(i)=='X'){
                tmpxl++;
            }
            if(board[i].charAt(2-i) == 'O'){
                tmpr++;
            }else if(board[i].charAt(2-i) =='X'){
                tmpxr++;
            }
            
            // ox의 개수를 세기 위한 반복문
            for(int y = 0; y < board.length; y++){
                if(board[i].charAt(y) == 'O'){
                    arro[y]+=1;
                    cnto++;
                }else if(board[i].charAt(y) == 'X'){
                    arr[y]+=1;
                    cntx++;
                }
            }
        }
        
        // 각 열이 3개면 정답
        for(int i = 0; i < arr.length; i++){
        	// 각 열의 x의 개수가 3이면 정답
            if(arr[i] == 3){
                x = true;
            }
            // 각 열의 o의 개수가 3이면 정답
            if(arro[i] == 3){
                o = true;
            }

        }
        // 각 대각선에 3개가 있으면 정답
        if(tmpl == 3 || tmpr == 3){
            o = true;
        }else if(tmpxl == 3 || tmpxr == 3){
            x = true;
        }
		
        // o의 개수가 x보다 1만 크고, 이 때 x는 정답이 나오지 말아야함
        if(cnto==cntx+1 && x == false){
            answer = 1;
        // o와 x의 개수가 같다면
        }else if(cnto == cntx){
        	// o는 정답이 나와서는 안됨(o가 선공이므로)
            if(o==false){
                answer = 1;
            }else{
                answer = 0;
            }
        // 그 외의 경우는 다 말이 안되는 게임임
        }else{
            answer = 0;
        }
        return answer;
    }
}
profile
꾸준히 하자!

0개의 댓글