더닝 크루거 효과를 아시나요? 특정 분야에 대해서 조금 아는 사람은 자신을 과대평가하고 적당히 아는 사람은 자신을 과소평가한다는 인지 편향이라고 합니다. 구글링 해보면 그 효과에 대해서 공감하는 사람이 너무 많다 보니 정작 실제로 발표된 논문과는 다른, 아주 조금 알 땐 모든 것을 아는 듯 행세하다가 조금 배울수록 급격히 그 자신감이 떨어지는 그래프를 찾아볼 수 있습니다.
최근 JS 공부를 시작하고 나니, 내가 바로 그 아주 조금 알던 사람이었구나 하는 생각이 참 많이 듭니다. 공부를 하면 할수록 천재는 많고 배워야 할 내용또한 많다는 게 느껴집니다.
누군가에게 가이드라인을 제시하는 내용도 아니고, 단순한 코드 리뷰로 많이 부족한 내용이지만 찾아오신분께 도움이 되었으면 좋겠습니다.
몇몇 문제를 풀다보니 그때그때 새로운 변수를 만들어서 사용하고 사용되는 리스트를 자꾸 늘려가니 말 그대로 "억지로 푼다"라는 느낌이 들어 최근에는 문제를 풀기 전, 이 문제에서 어떤 모듈을 사용해서 문제를 풀어나갈지 생각해보고 풀게 되었습니다.
만약 풀이 해설을 위해 찾아오신분은 정답 코드에 앞서 어떤 동작을 수행하는 모듈을 만들고자 했는지,  또한 그 모듈을 어떻게 구성했는지를 먼저 보신다면 더욱 좋을 것이라 생각됩니다.

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

 

20061번: 모노미노도미노 2

모노미노도미노는 아래와 같이 생긴 보드에서 진행되는 게임이다. 보드는 빨간색 보드, 파란색 보드, 초록색 보드가 그림과 같이 붙어있는 형태이다. 게임에서 사용하는 좌표 (x, y)에서 x는 행,

www.acmicpc.net

  • Given
    • 모노도미노는 특수한 형태의 보드에서 진행되며 좌표(x, y)는 x행, y열을 의미한다.
    • 사용되는 블록은 1*1 & 1*2 & 2*1 크기의 블록이 사용된다.
    • 한 행이 모두 블록으로 채워지면 그 행은 사라지며, 1점을 획득한다.
    • 각 블록은 놓아지면 수직으로 떨어지며, 다른 블록과 맞닿으면 그 자리에 멈춘다.
    • 만약 0,1번 행에 블록이 놓이면 해당 보드의 6번행은 삭제되며 한 행씩 아래로 밀린다.
  • Input
    • 첫째 줄에 블록의 갯수 N ( 1 <= N <= 10,000 )가 주어진다
    • 둘째 줄부터 N 개의 줄에 걸쳐 데이터 [ t , x , y ] 가 주어지며 각 t의 의미는 다음과 같다
      • t = 1 : 1*1 블록을 (x,y) 에 놓는다
      • t = 2 : 1*2 블록을 (x,y), (x, y+1)에 놓는다
      • t = 3 : 2*1 블록을 (x,y), (x+1, y)에 놓는다
  • Output
    • 첫째 줄에 모든 블록을 놓았을 때 얻는 점수를 출력하라
    • 둘째 줄에 두 보드에 블록에 의해 채워져있는 칸의 개수를 출력하라
  • Approach
    • 세로방향 보드와 가로방향 보드를 겹쳐놓았지만,  블록이 놓이는 방향을 치환하여 가로방향 보드를 분리하여 접근.


  1. 가로 세로 모두 사용될 공동 모듈
    1. 배치된 블록을 떨어뜨리기
    2. 모두 채워진 행이 있는지 확인 후, 해당 행 지우도 블록 내리기
    3. 0,1번 행에 타일이 위치하면 6번행 지우기
  2. 가로 보드에 사용될 치환 모듈
    1. 주어진 타일을 가로 보드에 맞게 치환하여 배치

  1. 사용될 데이터 테이블의 타입과 형태
    1. 블록이 놓일 각 보드의 형태는 Boolean 형태의 2차원 리스트로 설정
    2. 블록의 Type에 따라 블록이 놓이는 위치 데이터 lot = [[x, y]]
  2. 사용될 모듈의 순서
    1. 블록의 배치 후 떨어뜨리기
    2. 채워진 행 확인 후 점수 획득
    3. 1,2 행에 위치한 블록이 있는지 확인
    4. 반복

 


내 경우엔 이렇게 각각 기능을 나누어서 모듈화 시키는 게 디버그 시간을 많이 줄여주는 것 같다.

# import sys
# input = sys.stdin.readline
# input 을 변환해주면 좀 더 빠르겠지만 정작 나는 변환 안하고 풀었음에도 시간이 넉넉하게 남았었다

n = int(input())
get = []
for i in range(n):
    get.append(list(map(int,input().split())))
point = 0
sectorPoint = 0
Block = [[[False]*4 for _ in range(6)] for _ in range(2)] # Block[0] : 초록보드 ,Block[1] : 파란보드

우선 데이터의 선언과 입력부다. BOJ 에서는 input을 readline으로 치환하여 푸는 것이 시간을 절약하는 경우가 많아서 자주 사용하지만, 지금 코드 리뷰를 하며 다시 돌아보니 정작 나는 sys를 import 만 하고 변환하지 않고 풀었음에도 시간이 넉넉히 남았다.

def GtoB(lot):
    temp = []
    for x,y in lot: #lot = given
        temp.append([y,(3-x)])
    return temp
    
def blockdown(t,x,y,block,gnb):
    if gnb == 1:
        if t == 1:
            lot = [[x,y]]   
        elif t == 2:
            lot = [[x,y],[x,y+1]]
        else:
            lot = [[x,y],[x+1,y]]
        lot = GtoB(lot)
    else:    
        if t == 1:
            lot = [[x,y]]
        elif t == 2:
            lot = [[x,y],[x,y+1]]
        else:
            lot = [[x,y],[x+1,y]]

    stopPosition = 5 #블록을 위치시킬 위치
    for row,col in lot:#정지위치 찾기
        temp = [i[col] for i in block]
        for i in range(len(temp)):
            if temp[i]:
                stopPosition = min(i-1,stopPosition)
     
    for row,col in lot:
        if gnb == 1 and t == 2:
            block[stopPosition][col],block[stopPosition-1][col] = True, True
            return
        if t == 3 and gnb != 1:
            block[stopPosition][col],block[stopPosition-1][col] = True, True
            return
        block[stopPosition][col] = True
    return

GtoB 모듈은 현재 문제에서 세로 보드인 초록 보드에 놓이는 블록의 모양과 위치를 파란 보드의 기준에 맞게 변환해주는 모듈이다. 딱히 그 이상의 역할은 없다.

blockdown 모듈은 보드를 argument로 넣어주어서 원하는 보드에서 개별적으로 작동할 수 있도록 작성했다.
그래서 gnb를 argument 로 주어서 초록 보드인지 파란 보드인지 알려주어 블록 위치 리스트 (lot)를 구하고 구해진 블록위치 리스트 (lot) 를 통해 이번에 놓인 블록이 정지해야 할 y값 (stopPosition)을 구한다.
마지막으로 두 가지 특수한 상황을 제외하고 블록이 놓여야 할 자리를 True로 바꾸어준다.

def getPoint(block):
    global point
    flag = False
    for i in range(len(block)):
        if False in block[i]:
            continue
        block.remove(block[i])
        block.insert(0,[False]*4)
        point+=1
        flag = True
        break
    if flag:
        getPoint(block)

def CheckTheTopBlock(block):

    for i in range(2):
        if True in block[i]:
            block.pop()

    for i in range(6-len(block)):
        block.insert(0,[False]*4)

def settle(All):
    temp = 0
    for sector in All:
        for i in sector:
            for j in i:
                if j:
                    temp += 1
    
    return temp

각 블록이 배치되고 나서 실행될 채워진 행 확인 모듈 getPoint()와 1,2 행에 위치한 블록을 확인해 줄 CheckTheTopBlock() , 모든 작업이 끝나고 남은 True 블록을 세어주는 settle()의 코드인데 딱히 할 말이 없다. 그냥 인덱스 정해서 해당 칸이 True 혹은 False 이면 반환될 값을 더하거나 해당 행을 지워주는 방식이다.

for t,x,y in get:
    for i in range(2):
        blockdown(t,x,y,Block[i],i)
        getPoint(Block[i])
        CheckTheTopBlock(Block[i])
sectorPoint = settle(Block)
print(point)
print(sectorPoint)

[[ 마무리 ]] 

구현 문제가 흔히 그런지 모르겠지만 풀 땐 자잘한 디버그 하느라 정신없다가도 다 풀고 나서 리뷰할 땐 너무 보잘것없어서 내가 왜 그렇게 헤맸나 싶기도 합니다. 그래서 그 헤맴을 조금이라도 최소화해주는 게 문제를 푸는데 필요한 기능을 정리하고 그 기능을 수행하는 모듈을 하나씩 만들어가는 방식이 아닐까 싶습니다. 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기