https://school.programmers.co.kr/learn/courses/30/lessons/160585
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
이번 문제는 게임 과정에서 생길 수 있는 모든 반례를 찾는 문제로 판단된다.
문제를 간단하게 한번 분석해 보면 머쓱이가 혼자서 O와 X로 빙고 게임을 진행한다. 진행 중인 게임 상황이 입력으로 주어졌을 때, 그 게임이 정상적으로 나올 수 있는 상황이라면 1을 반환, 아니면 0을 반환하면 된다.
즉, 빙고 게임에서 나올 수 있는 모든 반례를 찾아서 해결해야 된다는 의미이다.
문제 분석
1. 후공(X)이 선공(O) 보다 먼저 나온 경우
이 경우는 간단하게 해결할 수 있다. 후공이 만약 선공보다 많이 나왔다면 X의 개수가 O의 개수보다 많을 것이다.
if (xCount > oCount) {
return 0;
}
2. 게임 순서가 선공(O), 후공(X) 한 번씩 번갈아서 진행되지 않은 경우
말이 조금 어려울 수 있는데, 선공 -> 후공 -> 선공 -> 후공 이렇게 되어야 하는데, 선공 -> 선공 -> 후공 -> 선공 이런 식으로 되었을 때를 처리해줘야 한다.
후공(X)이 여러번 나왔을 경우는 1번 반례에서 해결이 되기 때문에 선공(O)이 여러 번 나왔을 경우만 신경 써주면 된다. 순서대로 게임이 진행되었다면 선공(O) 개수에서 후공(X) 개수를 뺐을 때 1 이하의 수가 나와야 한다.
if (oCount - xCount > 1) {
return 0;
}
3. 빙고를 다 맞춰 게임이 종료되었을 때의 조건
3.1 선공(O)이 승리한 경우
선공이 승리했을 경우를 보면 항상 O의 개수가 X의 개수보다 많아야 한다. 이 반대는 모두 불가능한 경우의 수이다.
if (O가 승리했을 때) {
if (oCount <= xCount) {
return 0;
}
}
3.2 후공(X)이 승리한 경우
후공이 승리했을 경우를 보면 항상 O의 개수와 X의 개수가 동일하다. 개수가 다르다면 모두 불가능한 경우의 수이다.
if (X가 승리했을 때) {
if (oCount != xCount) {
return 0;
}
}
3.3 둘 다 승리한 경우
이 경우를 가장 먼저 넣어줘야 한다. O도 승리하고, X도 승리했을 경우이다. 말이 안 되기 때문에 바로 0을 리턴해주자.
if (둘 다 승리했을 때) {
return 0;
}
처음에 풀었던 풀이
class Solution {
public int solution(String[] board) {
int oCnt = 0;
int xCnt = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[i].charAt(j) == 'O') {
oCnt++;
} else if (board[i].charAt(j) == 'X') {
xCnt++;
}
}
}
// 후공이 선공보다 먼저 나온경우
if (xCnt > oCnt) {
return 0;
}
// 순서대로 하나씩 증가하지 않은 경우
if (oCnt - xCnt > 1) {
return 0;
}
// 게임이 끝났는데 계속 진행된 경우
String winner = getWinner(board);
if (winner.equals("Error")) {
return 0;
}
if (winner.equals("oWin")) {
if (oCnt <= xCnt) {
return 0;
}
}
if (winner.equals("xWin")) {
if (oCnt != xCnt) {
return 0;
}
}
return 1;
}
public String getWinner(String[] board) {
boolean oWin = false;
boolean xWin = false;
for (int i = 0; i < 3; i++) {
// 가로로 일치하는 경우
if (board[i].equals("OOO")) {
oWin = true;
}
// 세로로 일치하는 경우
if (board[0].charAt(i) == 'O'
&& board[0].charAt(i) == board[1].charAt(i)
&& board[1].charAt(i) == board[2].charAt(i)) {
oWin = true;
}
}
if (board[0].charAt(0) == 'O'
&& board[0].charAt(0) == board[1].charAt(1)
&& board[1].charAt(1) == board[2].charAt(2)) {
oWin = true;
}
if (board[0].charAt(2) == 'O'
&& board[0].charAt(2) == board[1].charAt(1)
&& board[1].charAt(1) == board[2].charAt(0)) {
oWin = true;
}
for (int i = 0; i < 3; i++) {
// 가로로 일치하는 경우
if (board[i].equals("XXX")) {
xWin = true;
}
// 세로로 일치하는 경우
if (board[0].charAt(i) == 'X'
&& board[0].charAt(i) == board[1].charAt(i)
&& board[1].charAt(i) == board[2].charAt(i)) {
xWin = true;
}
}
if (board[0].charAt(0) == 'X'
&& board[0].charAt(0) == board[1].charAt(1)
&& board[1].charAt(1) == board[2].charAt(2)) {
xWin = true;
}
if (board[0].charAt(2) == 'X'
&& board[0].charAt(2) == board[1].charAt(1)
&& board[1].charAt(1) == board[2].charAt(0)) {
xWin = true;
}
if (oWin && xWin) {
return "Error";
} else if (oWin) {
return "oWin";
} else if (xWin) {
return "xWin";
}
return "noWin";
}
}
getWinner()라는 메소드를 만들어서 둘 다 승리했다면 "Error"를 반환하고, 선공이 승리했다면 "oWin"을 반환하고, 후공이 승리했다면 "xWin"을 반환하도록 했다.
문제는 모두 통과되었지만 이렇게 작성했을 때 딱 눈에 보기에도 반복되는 코드가 너무 많고, 가독성이 떨어졌던 문제가 있었다.
문제점 개선 후 리펙토링한 코드
class Solution {
private final char O = 'O';
private final char X = 'X';
public int solution(String[] board) {
int oCnt = 0;
int xCnt = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[i].charAt(j) == O) {
oCnt++;
} else if (board[i].charAt(j) == X) {
xCnt++;
}
}
}
// 후공이 선공보다 먼저 나온경우
if (xCnt > oCnt) {
return 0;
}
// 순서대로 하나씩 증가하지 않은 경우
if (oCnt - xCnt > 1) {
return 0;
}
// 게임이 끝났는데 계속 진행된 경우
boolean oWin = getWinner(board, O);
boolean xWin = getWinner(board, X);
if (oWin && xWin) {
return 0;
}
if (oWin && oCnt <= xCnt) {
return 0;
}
if (xWin && oCnt != xCnt) {
return 0;
}
return 1;
}
public boolean getWinner(String[] board, char value) {
for (int i = 0; i < 3; i++) {
// 가로로 일치하는 경우
if (board[i].charAt(0) == value
&& board[i].charAt(0) == board[i].charAt(1)
&& board[i].charAt(1) == board[i].charAt(2)) {
return true;
}
// 세로로 일치하는 경우
if (board[0].charAt(i) == value
&& board[0].charAt(i) == board[1].charAt(i)
&& board[1].charAt(i) == board[2].charAt(i)) {
return true;
}
}
// 대각선 일치
if (board[0].charAt(0) == value
&& board[0].charAt(0) == board[1].charAt(1)
&& board[1].charAt(1) == board[2].charAt(2)) {
return true;
}
if (board[0].charAt(2) == value
&& board[0].charAt(2) == board[1].charAt(1)
&& board[1].charAt(1) == board[2].charAt(0)) {
return true;
}
return false;
}
}
1. 자주 사용했던 'O'와 'X'는 상수로서 처리해 줬다. 훨씬 가독성이 증가한 것을 느낄 수 있었다.
2. boolean 타입을 반환하는 메서드로 변경하여 선공, 후공 승리를 나누었다. 이렇게 해서 코드의 반복을 줄일 수 있었다.
한눈에 보기에도 깔끔해진 코드로 작성할 수 있었다.
'🧑🏻💻 Dev > 알고리즘' 카테고리의 다른 글
[백준] 1931번 회의실 배정 - Java (0) | 2023.05.14 |
---|---|
[백준] 1260번 DFS와 BFS 자바 (0) | 2023.04.28 |
[프로그래머스] 레벨 1 문제 모두 풀고난 후 배운점 (2) | 2023.03.17 |
[프로그래머스] 시소 짝꿍 (Java) (0) | 2023.02.15 |
[Sorfteer] 인증평가(5차) 기출 - 성적 평가 (Java) (0) | 2023.02.07 |