컴퓨터는 잘못이 없다..

[알고리즘]구현_게임게발(파이썬 2차원리스트를 리스트 컴프리헨션 문법으로 초기화/append()와 extend()/함수 내 global변수) 본문

공부/알고리즘(파이썬)

[알고리즘]구현_게임게발(파이썬 2차원리스트를 리스트 컴프리헨션 문법으로 초기화/append()와 extend()/함수 내 global변수)

도토리까꿍v 2020. 12. 4. 02:03
Contents 접기

['게임개발' 문제 설명]

-난이도 : ★

-풀이시간 : 40분

-시간 제한 : 1초

-메모리 제한 : 128MB

-출처 : 이것이 코딩테스트다(p.118)

 

-문제 : 

현민이는 게임 캐릭터가 맵 안에서 움직이는 시스템을 개발중이다. 캐릭터가 있는 장소는 1x1 크기의 정사각형으로 이뤄진 NxM 크기의 직사각형으로, 각각의 칸은 육지 또는 바다이다. 캐릭터는 동서남북 중 한 곳을 바라본다.

맵의 각 칸은 (A,B)로 나타낼 수 있고, A는 북쪽으로부터 떨어진 칸의 개수, B는 서쪽으로부터 떨어진 칸의 개수이다.

캐릭터는 상하좌우로 움직일 수 있고, 바다로 되어 있는 공간에는 갈 수 없다.

캐릭터의 움직임을 설정하기 위해 정해 놓은 메뉴얼은 이러하다.

 

1. 현재 위치에서 현재 방향을 기준으로 왼쪽 방향(반시계 방향으로 90도 회전한 방향)부터 차례대로 갈 곳을 정한다.

 

2. 캐릭터의 바로 왼쪽 방향에 아직 가보지 않은 칸이 존재한다면, 왼쪽 방향으로 회전한 다음 왼쪽으로 한 칸을 전진한다. 왼쪽 방향에 가보지 않은 칸이 없다면, 왼쪽 방향으로 회전만 수행하고 1단계로 돌아간다.

 

3. 만약 네 칸이 모두 이미 가본 칸이거나 바다로 되어 있는 칸인 경우에는, 바라보는 방향을 유지한 채로 한 칸 뒤로 가고 1단계로 돌아간다. 단, 이때 뒤쪽 방향이 바다인 칸이라 뒤로 갈 수 없는 경우에는 움직임을 멈춘다. 

 

현민이는 위 과정을 반복적으로 수행하면서 캐릭터의 움직임에 이상이 있는지 테스트하려고 한다. 메뉴얼에 따라 캐릭터를 이동시킨 뒤에 캐릭터가 방문한 칸의 수를 출력하는 프로그램을 만드시오.

 

-입력조건: 

첫째 줄에 맵의 세로 크기 N과 가로 크기 M을 공백으로 구분하여 입력한다. (3<=N,M<=50)

둘째 줄에 게임 캐릭터가 있는 칸의 좌표(A,B) 와 바라보는 방향 d가 각각 서로 공백으로 구분하여 주어진다.

방향 d의 값으로는 다음과 같이 4가지가 존재한다.

-0:북쪽

-1:동쪽

-2:남쪽

-3:서쪽

 

셋째 줄부터 맵이 육지인지 바다인지에 대한 정보가 주어진다. N개의 줄에 맵의 상태가 북쪽부터 남쪽 순서대로, 각 줄의 데이터는 서쪽부터 동쪽 순서대로 주어진다. 맵의 외곽은 항상 바다로 되어있다.

-0:육지

-1:바다

 

처음에 게임 캐릭터가 위치한 칸의 상태는 항상 육지이다.

 

-출력조건: 첫째 줄에 이동을 마친 후 캐릭터가 방문한 칸의 수를 출력한다.

 

-입력예시1

4 4        #4x4 맵 생성
1 1 0      #1,1에 북쪽을 바라보고 있는 캐릭터
1 1 1 1    
1 0 0 1
1 1 0 1
1 1 1 1

-출력예시1

3

 

['게임개발' 답안1]

#n,m을 공백으로 구분하여 입력받기
n, m = map(int, input().split())

#방문한 위치를 저장하기 위한 맵을 생성하여 0으로 초기화하기,2차원 리스트
d = [[0]*m for _ in range(n)] #n*m 행렬만들고 0으로 채워넣기 방문안함:0,방문함:1

#현재 캐릭터의 x,y 좌표와 방향을 입력받기
x, y, direction = map(int, input().split())

#2차원 리스트인 d에 현재 위치 방문처리하기
d[x][y] = 1

#전체 맵 정보를 입력받기
#빈 리스트를 만들기
array = []
for i in range(n):
    # 이런식으로 array.append()하면
    # [[1,1,1,1],[1,0,0,1],[1,1,0,1],[1,1,1,1]]
    # 로 2차원 배열이 만들어 질것임!!
    array.append(list(map(int, input().split())))


#북, 동, 남, 서에 따른 방향크기를 정의한다.
#북은 위쪽, 동은 오른쪽, 남은 아래쪽, 서는 왼쪽임
#(행x, 열y)의 이동에 따른 증감을 생각해보자!
dx = [-1, 0, 1, 0]
dy = [0, 1, 0, -1]

#왼쪽으로 회전
def turn_left():
    #함수 안에서 전역변수의 값을 변경하려면 global 키워드를 써야한다.
    #global 키워드를 쓰지 않으면 direction은
    #turn_left 함수의 지역변수가 되어버린다.
    global direction
    direction -=1
    if direction == -1 :
        direction = 3
    #direction은 북 0 ,동 1,남 2,서 3
    #동,서,남,북 왼쪽으로 회전할 수록 수가 작아짐!
    #단, 북쪽에서 왼쪽으로 turn 하면 -1이 되므로 그때만 서쪽인 3으로 고정해주기


#시뮬레이션 시작
count =1 #시작지점은 count하고 시작
turn_time = 0
while True :
    #왼쪽으로 회전
    turn_left()
    nx = x + dx[direction]
    ny = y + dy[direction]
    #회전한 이후 정면에 가보지 않은 칸이 존재하면서 땅인 경우
    if d[nx][ny] == 0 and array[nx][ny] == 0 :
        d[nx][ny] = 1 #방문처리
        x, y = nx, ny #실제로 이동
        count+=1 #카운트 증가
        turn_time=0 #turn_time은 다시 0
        continue
    #회전한 이후 정면에 가보지 않은 칸이 없거나 바다인 경우
    else :
        #증가시키고 아래 if문에서 걸리지 않는다면
        #무한루프 이므로 맨위로 돌아가서 turn_left()부터 수행한다.
        turn_time +=1 #turn_time을 증가시키기
    #네 방향 모두 갈 수 없는 경우
    if turn_time == 4 :
        #바라보는 방향 그대로 하고 뒤로 한칸 가기
        nx = x-dx[direction] #바라보는 방향 반대이므로 부호 바꾸고 더해주면 된다.
        ny = y-dy[direction]
        #뒤로 갈 수 있다면 이동하기, 땅이라면 이동가능
        if array[nx][ny] == 0 :
            x,y = nx, ny
        #뒤가 바다로 막혀있다면 움직임을 멈춘다.
        else :
            #반복문을 빠져나간다.
            break
        #뒤로 갈 수 있다면 이동 후 turn_time을 초기화 해주어야한다.
        turn_time=0

#count 출력
print(count)



 

[설명]

문제풀이를 위한 중요한 테크닉

 

현재 캐릭터가 북쪽을 바라보고 있을 때는 북쪽으로 이동하기 위해 x 와 y 좌표를 각각 dx[0], dy[0] 만큼 더한다.

다시 말해 현재 위치에서 (-1, 0) 만큼 이동시키는 것이다. 이처럼 코드를 작성해보자!

 

 

 

[파이썬]

-리스트 컴프리헨션 문법1 : d=[[0]*3 for _ in range(5)] 의 의미(★뒷숫자가 행, 앞숫자가 열, 뒤에서부터 읽어서 5*3)

[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]

다르게 표현하면

[[0, 0, 0],

[0, 0, 0],

[0, 0, 0],

[0, 0, 0],

[0, 0, 0]]

-> 5*3 행,열을 만들고 내용물은 0으로 채워넣어라 

 

 

-리스트 컴프리헨션 문법2 : d=[[0] for _ in range(5)] 의 의미(뒤에서부터 읽어서 5*1) 
[[0], [0], [0], [0], [0]]

다르게 표현하면

[[0], 

[0], 

[0], 

[0], 

[0]]

-> 5*1 행,열을 만들고 내용물은 0으로 채워넣어라 

 

 

-2차원 리스트에서 

 

-append() 함수와 extend() 함수

d = [1,2] 에 

d.append([3,4])를 하면 [1,2,[3,4]] 로 들어가고

d.extend([3,4])를 하면 [1,2,3,4] 로 들어가진다. 

 

 

-함수 내 global변수

방법1) global direction으로 선언해주면 함수 호출 시 함수밖의 direction을 변경할 수 있다. 

방법2) global direction으로 하고 싶지 않다면 direction 연산 후 return하여 함수밖의 direction에 대입해주는 방식으로 사용해야 한다. 

방법1의 경우 함수를 호출하기만 하면 함수밖의 direction이 변경되고

방법2의 경우 함수를 호출하여 그 리턴값을 함수밖의 direction에 대입해주어야 함수밖의 direction이 변경된다.

 

#방법1사용

a=3
def sum():
global a
a+=2 #이때 a는 바깥 a

sum()

print(a) #5출력


# 방법2사용

a=3
def sum():
b = a+2 #이때 a는 바깥 a 
return b

a=sum()
print(a) #5출력

 

# 오류나는 경우 

a=3
def sum():
a+=2 #a에 빨간줄이 뜬다.
return a #a에 빨간줄이 뜬다.

a=sum()
print(a) #5출력

 

#추가로 알아둘 것

 

1. 함수 바깥에 선언한 변수를 함수 안에서도 사용가능

x = 10          # 전역 변수
def foo():
    print(x)    # 전역 변수 출력
 
foo()
print(x)        # 전역 변수 출력

#결과
10
10

▲코드 설명

파이썬은 함수 바깥의 부분을 모두 전역범위로 보고 전역 변수라고 본다. 

따라서 함수 바깥에 선언한 변수를 함수 안에서도 쓸 수 있다.

 

2. 함수 바깥에 선언한 변수의 값을 사용할 수는 있지만 직접 값을 바꿀 순 없다. 아래 예제를 보자.

x = 10  # 전역 변수


def foo():
    x=3
    print(x)  # 전역 변수 출력


foo()
print(x)  # 전역 변수 출력

#결과
3
10

▲함수 안에서 x의 값을 바꾸어주었지만 함수바깥의 변수 x는 변하지 않았다. 이렇게 할 경우 함수 안의 x는 지역변수가 된다. 

 

3. 함수 바깥의 변수의 값을 함수 내에서 바꾸어주고싶다면? -> global!

x = 10  # 전역 변수


def foo():
    global x
    x=3
    print(x)  # 전역 변수 출력


foo()
print(x)  # 전역 변수 출력

▲이처럼 함수 내에서 global x로 먼저 선언하면 바깥의 전역변수를 의미하며 함수 내, 함수 바깥의 변수 x가 동일하게 바뀌는 것을 확인할 수 있다. 

Comments