다음과 같은 미로가 maze1.txt라는 텍스트 파일에 작성되어있다.
이때 해당 파일을 읽어 미로를 화면에 표시하는 프로그램을 작성한다.
⚡️ 프로그램의구성
⚡️ 조건
f = open('maze2.txt', 'r') # 파일 읽기 모드로 열기
line = f.readline() # 첫 줄(ex. 9X12, 10X10)은 생략
width = int(line.split()[0]) # 미로의 가로(너비)값 저장
height = int(line.split()[1]) # 미로의 세로(높이)값 저장
우선 미로를 담고 있는 파일을 읽기 모드로 열어준 뒤, 미로의 첫번째 줄(ex. “9x12”, “10x10”)은 생략해준다. (굳이 필요 없으므로)
그리고 미로의 가로(너비)와 세로(높이)를 구하여 각각 width와 height 변수에 넣어준다.
maze = [] # 미로의 정보를 담을 리스트
line_Num = 0 # 행 번호
while True:
disk = []
line = f.readline().strip()
line_Num += 1
if not line: break
# 홀수 행일 경우
# 각 행의 짝수번째 요소들만 남겨준다
if line_Num % 2 == 1:
for i in range(len(line))[1::2]:
disk.append(line[i])
disk = ("".join(disk))
# 짝수 행일 경우
# 각 행의 홀수번째 요소들만 남겨준다
else:
for i in range(len(line))[0::2]:
disk.append(line[i])
disk = ("".join(disk))
다음으론 미로의 데이터를 가공하여 담아 줄 리스트 maze를 선언해준다.
그 후 텍스트 파일을 한 줄씩 가져와 line에 저장한다. 이때, line_Num을 통해 각 행의 번호를 파악하고,
홀수 행일 경우에는 각 행의 짝수 번째 요소들만 남겨준다.
이렇게 하는 이유는 홀수 행의 홀수 번째 요소들은 모두 +, -, | 문자들로 이루어져 있으므로,
해당 위치가 공백이 아니라 문자로 이루어져 있다는 사실만 인지하고 있으면 되기 때문이다.
(ex. "+---+---+" -> "----")
비슷한 이유로 짝수 행일 경우, 각 행의 홀수 번째 요소들만 남겨주는데,
짝수 행의 짝수 번째 요소들은 모두 공백 문자이기 때문에 굳이 저장할 필요가 없다.
(ex. "| | | |" -> "| | | |")
str = disk.replace('-', '1') # 각 줄의 '-', '|'기호를 '1'로 변환
str1 = str.replace('|', '1')
str2 = str1.replace(' ', '0') # 각 줄의 공백 문자를 '0'으로 변환
이제 이렇게 가공된 행들의 -, | 문자는 숫자 1로, 공백 문자는 숫자 0으로 치환하여 각 행을 이진수 형태로 변환해준다.
dec_Num = int(str2, 2) # 각 이진수를 10진수로 변환한 값을 저장할 변수
maze.append(dec_Num) # 변환된 10진수를 maze에 저장
위의 과정들을 거쳐 미로 파일의 각 행(문자열)들은 하나의 이진수로 변환되었는데,
이렇게 얻어진 이진수들을 10진수 dec_Num으로 변환한 후 maze에 차례로 넣어준다.
이때 주어진 미로의 규격이 9x12이므로 각 행의 이진수들은 모두 9~10자리수 이므로 9~11비트 범위 내이다.
이는 바이트로 환산해보아도 2바이트가 채 되지 않아 short범위 내에 속한다.
즉 위의 과정들을 거쳐 각 행을 2바이트 이내의 용량으로 저장할 수 있게 된 것이고
이는 기존의 저장 방식(각 행의 용량: 19바이트)에 비해 훨씬 적은 용량으로 미로의 데이터를 저장할 수 있게 되었음을 의미한다.
미로의 저장 공간의 크기는 최대 50바이트가 될 것이다. (2바이트(short의 크기) * 25(총 행의 개수) = 50)
# maze에 저장된 데이터를 바탕으로 미로를 다시 출력해 줄 함수
def draw_maze(maze, width, height):
output = [] # output list 생성
row_C = width * 2 + 1 # 미로의 실제 가로 길이(너비)(텍스트 파일 기준)
col_C = height * 2 + 1 # 미로의 실제 세로 길이(높이)(텍스트 파일 기준)
for i in range(len(maze)):
# maze에 저장되어있던 각 10진수들을 다시 2진수로 변환
bi_Num = list(bin(maze[i])[2:].zfill(width))
우선 maze에 저장된 데이터를 바탕으로 미로를 다시 출력해 줄 함수 draw_maze를 선언하고,
maze에 저장되어 있던 각 10진수들을 다시 2진수 bi_Num으로 변환해준다.
if i % 2 == 0:
bi_Num = "1".join(bi_Num)
full_Bi = list(bi_Num)
full_Bi.insert(0, '1')
full_Bi.append('1')
이때 홀수 행의 경우, 생략했던 홀수 번째 요소들(1)을 복구시켜 full_Bi에 담아준다.
else:
bi_Num = "0".join(bi_Num)
full_Bi = list(bi_Num)
짝수 행의 경우, 생략했던 짝수 번째 요소들(0)을 복구시켜 full_Bi에 담아준다.
위의 과정들을 거쳐 maze에 가공된 형태로 저장되어 있던 데이터는 원래 미로의 너비와 같은 길이의 이진수로 변환된다.
output.append(full_Bi)
그리고 이렇게 복구된 이진수들을 output이라는 2차원 리스트에 차례로 담아준다.
이때 변환된 이진수들을 (ex. 1000101001) 1차원 리스트(ex. [ '1000101001' , '1010001010' ] ) 형태로 저장하지 않고
2차원 리스트(ex. [ [1,0,0,0,1,0,1,0,0,1] , [1,0,1,0,0,0,1,0,1,0] ] ) 형태로 저장하는 이유는,
한 줄의 데이터에서 짝수 번째 요소와 홀수 번째 요소를 나누어서 데이터를 변환해야 하는데, 2차원 리스트가 이에 용이하기 때문이다.
이제 출력할 차례이다.
이진수 형태로 저장된 미로를 다시 실제 미로처럼 바꾸는 과정은 다음과 같다.
1. 미로의 가장자리 벽 4변을 실제 미로 모양으로 바꾼다.
# 위쪽 가장자리 벽 정리
for j in range(row_C):
if output[1][j] == '1':
del output[0][j]
output[0].insert(j, '+')
else:
del output[0][j]
output[0].insert(j, '-')
# 아래쪽 가장자리 벽 정리
if output[-2][j] == '1':
del output[-1][j]
output[-1].insert(j, '+')
else:
del output[-1][j]
output[-1].insert(j, '-')
# 왼쪽 가장자리 벽 정리
for i in range(col_C)[1:-1]:
if output[i][1] == '1':
del output[i][0]
output[i].insert(0, '+')
else:
del output[i][0]
output[i].insert(0, '|')
# 오른쪽 가장자리 벽 정리
if output[i][-2] == '1':
del output[i][-1]
output[i].append('+')
else:
del output[i][-1]
output[i].append('|')
# 홀수행 정리
# 홀수행 중 짝수열 값 정리
for i in range(col_C)[2:-1:2]:
for j in range(row_C)[1::2]:
if output[i][j] == '1':
del output[i][j]
output[i].insert(j, '-')
else:
del output[i][j]
output[i].insert(j, ' ')
# 홀수행 중 홀수열 값 정리
for j in range(row_C)[2:-1:2]:
if output[i][j-1] == '-' and output[i][j+1] == '-' \
and output[i+1][j] == ' ' and output[i-1][j] == ' ':
del output[i][j]
output[i].insert(j, '-')
elif output[i-1][j] == '|' and output[i+1][j] == '|' \
and output[i][j-1] == ' ' and output[i][j+1] == ' ':
del output[i][j]
output[i].insert(j, '|')
else:
del output[i][j]
output[i].insert(j, '+')
# 미로 출력
for i in output:
print("".join(i))
마지막으로 이중리스트 형태로 저장된 output을 join메소드를 통해서 하나의 문자열로 통합한 후 출력해준다.
위와 같은 방법으로 10진수로 저장되어 있던 데이터를 다시 원래 미로의 형태로 출력할 수 있었다.
f = open('maze1.txt', 'r') # 파일 읽기 모드로 열기
line = f.readline() # 첫 줄(ex. 9X12, 10X10)은 생략
width = int(line.split()[0]) # 미로의 가로(너비)값 저장
height = int(line.split()[1]) # 미로의 세로(높이)값 저장
maze = [] # 미로의 정보를 담을 리스트
line_Num = 0 # 행 번호
while True:
disk = []
line = f.readline().strip() # 2번째 줄 부터 끝까지 파일의 정보를 한 줄씩 가져 온다
line_Num += 1
if not line: break
# 홀수 행일 경우
# 각 행의 짝수번째 요소들만 남겨준다
# why? -> 홀수 행의 홀수번째 요소들은 모두 '+'혹은 '-'문자로 이루어져 있기 때문에, 해당 위치에 문자가 존재 한다는 사실만 알고 있으면 되기 때문
# ex. "+---+---+" -> "----"
if line_Num % 2 == 1:
for i in range(len(line))[1::2]:
disk.append(line[i])
disk = ("".join(disk))
# 짝수 행일 경우
# 각 행의 홀수번째 요소들만 남겨준다
# why? -> 짝수 행의 짝수번째 요소들은 모두 공백 문자이기 때문에 굳이 저장할 필요가 없다.
# ex. "| | | |" -> "| | | |"
else:
for i in range(len(line))[0::2]:
disk.append(line[i])
disk = ("".join(disk))
str = disk.replace('-', '1') # 각 줄의 '-', '|'기호를 '1'로 변환
str1 = str.replace('|', '1')
str2 = str1.replace(' ', '0') # 각 줄의 공백 문자를 '0'으로 변환
#==================== 위의 과정을 거쳐 각 줄(문자열)은 이진수로 변환된다. =======================#
dec_Num = int(str2, 2) # 각 이진수를 10진수로 변환한 값을 저장할 변수
maze.append(dec_Num) # 변환된 10진수를 maze에 저장
f.close()
#============================ maze에 미로 데이터 입력 완료 =================================#
def draw_maze(maze, width, height): # maze에 저장된 데이터를 바탕으로 미로를 다시 출력해 줄 함수
output = [] # output list 생성
row_C = width * 2 + 1 # 미로의 실제 가로 길이(너비)(텍스트 파일 기준)
col_C = height * 2 + 1 # 미로의 실제 세로 길이(높이)(텍스트 파일 기준)
for i in range(len(maze)):
# 1. maze에 저장되어있던 각 10진수들을 다시 2진수로 변환
bi_Num = list(bin(maze[i])[2:].zfill(width))
# 2. 홀수행의 경우, 홀수번째 요소들을 복구시킨다.
if i % 2 == 0:
bi_Num = "1".join(bi_Num)
full_Bi = list(bi_Num)
full_Bi.insert(0, '1')
full_Bi.append('1')
# 3. 짝수행의 경우, 짝수번째 요소들을 복구시킨다.
else:
bi_Num = "0".join(bi_Num)
full_Bi = list(bi_Num)
# 즉, 위의 과정들을 거쳐 maze에 가공되어 저장되어 있던 데이터들은 원래 미로의 너비와 같은 길이의 이진수의 형태로 변환된다.
# 4. 복구된 리스트들을 output이라는 2중 리스트에 담아준다.
output.append(full_Bi)
# 미로의 첫번째 줄 정리
for j in range(row_C):
if output[1][j] == '1':
del output[0][j]
output[0].insert(j, '+')
else:
del output[0][j]
output[0].insert(j, '-')
# 미로의 마지막 줄 정리
if output[-2][j] == '1':
del output[-1][j]
output[-1].insert(j, '+')
else:
del output[-1][j]
output[-1].insert(j, '-')
# 왼쪽 가벽 정리
for i in range(col_C)[1:-1]:
if output[i][1] == '1':
del output[i][0]
output[i].insert(0, '+')
else:
del output[i][0]
output[i].insert(0, '|')
# 오른쪽 가벽 정리
if output[i][-2] == '1':
del output[i][-1]
output[i].append('+')
else:
del output[i][-1]
output[i].append('|')
#=========== 가벽 안의 값들 정리 =============#
# 짝수 행 정리
for i in range(col_C)[1::2]:
for j in range(row_C)[1:-1:]:
if output[i][j] == '1':
del output[i][j]
output[i].insert(j, '|')
else:
del output[i][j]
output[i].insert(j, ' ')
# 홀수 행 정리 (2가지 과정)
# 홀수 행 중 짝수열 값 정리
for i in range(col_C)[2:-1:2]:
for j in range(row_C)[1::2]: #홀수 행의 짝수 열이 '1'이면 '-' 아니면 공백
if output[i][j] == '1':
del output[i][j]
output[i].insert(j, '-')
else:
del output[i][j]
output[i].insert(j, ' ')
# 남은 홀수 행 중 홀수열 값 정리
for j in range(row_C)[2:-1:2]:
if output[i][j-1] == '-' and output[i][j+1] == '-' \
and output[i+1][j] == ' ' and output[i-1][j] == ' ': #홀수 행의 홀수 열 중 '-'가 되는 조건
del output[i][j]
output[i].insert(j, '-')
elif output[i-1][j] == '|' and output[i+1][j] == '|' \
and output[i][j-1] == ' ' and output[i][j+1] == ' ': #홀수 행의 홀수 열 중 '|'가 되는 조건
del output[i][j]
output[i].insert(j, '|')
else: #나머지는 '+'로 표시
del output[i][j]
output[i].insert(j, '+')
# 미로 출력
for i in output:
print("".join(i))
draw_maze(maze, width, height) # 가공하여 저장했던 미로 다시 출력