학습 목표
- 2차원 리스트의 구조를 이해할 수 있음
- 2차원 리스트의 생성 및 순회 구조를 설명할 수 있음
- 게임 루프를 생성할 수 있음
주요 용어
- 2차원 리스트
- 1차원 리스트의 행 인덱스와 내포된 1차원 리스트의 열 인덱스로 구성된 리스트
강의록
2차원 리스트
리스트
- 순서화 된 값의 집합체를 저장할 수 있는 데이터 타입
- 단일 식별자로 연속된 저장 공간 접근 수단 제공
- 반복 구조에서 많이 사용
- 단일 식별자로 연속된 저장 공간 접근 수단 제공
1차원 리스트
2차원 리스트
- 리스트에 리스트가 내포된 즉, 리스트의 리스트
- 값들이 서로 매칭 되어 일렬로 세울 수 없는 경우
2차원 리스트 구조
2차원 리스트 접근
- 2차원 리스트의 요소는
리스트이름[행인덱스][열인덱스]
형식으로 접근- 두 개의 접근 연산자를 사용해 개별적인 값에 접근
distance[0]
→ 첫 번째 행 리스트distance[0][0]
→ 첫 번째 행의 첫 번째 열 값distance[4]
→ 다섯 번째 행 리스트distance[7][7]
→ 여덟 번째 행의 여덟 번째 열 값
2차원 리스트 생성
구문 형식
- 리스트 내부에 리스트가 정의
- 2차원 리스트 생성 시 원소가 결정된 상황에 사용
2차원 리스트 동적 생성
random
모듈을 이용하여 동적으로 2차원 리스트 생성 가능1 2 3 4 5 6 7 8 9 10 11
import random as rd # 행의 개수 nRows, 열의 개수 nColumns distance = [] for i in range(nRows): row = [] for j in range(nColumns): row.append(rd.randint(0, 99)) distance.append(row) print(distance)
2차원 리스트 순회
- 각 차원 순차적으로 순회하기 위한 2개의 반복 구조가 중첩되어 사용
- 해당 차원의 개수에 맞게 끔 for문이 중첩 됨
출발 도시 변수와 도착 도시 변수를 사용하여 순회
1 2 3 4 5 6
# 출발도시(departure) 순회 for dep in distance: # dep 출발 도시 변수 # 도착도시(destination) 순회 for des in dep: # des 도착 도시 변수 print(des, end = " ") # 각 도시 간 거리를 출력 print() # 한 행의 출력이 끝나면 줄 바꿈
틱택토 게임 구현
게임 설계
- 2차원 리스트를 사용하여 게임 판을 생성하고 각각의 칸을 빈칸으로 초기화
- 빈칸에 대한 지정한 기호(*)를 사용
- 게임판에 빈칸이 남아있는지 확인
- 게임 판 전체를 확인하고 True/False 반환
- 둘 중 한 플레이어가 승리했는지 확인
- 8가지의 승리 상황에 따른 가능성을 확인
- 각각의 행, 열과 두 개의 대각선 방향을 확인
- 게임 판의 현재 상태를 출력
- 게임 시작
- 무작위로 선공할 플레이어를 선택
- 게임 루프를 기동
- 게임 판의 현재 상태를 출력하고 다음 플레이어가 빈칸을 선택
- 플레이어가 선택할 빈칸의 위치(행과 열 번호)를 입력 받음
- 플레이어가 선택한 위치에 기호를 표시하고 게임 판을 업데이트
- 현재 플레이어가 승리했는지 확인
- 게임 판에 빈칸이 남아있는지 확인
- 게임 판이 가득찬 경우, 무승부 메시지를 출력하고 게임 루프를 종료
- 승리 상황인 경우, 승리한 플레이어에 대한 메시지를 출력하고 게임 루프를 종료
- 사용자일 경우 사용자 입력을 통해 행과 열 번호를 입력 받음
- 컴퓨터일 경우 무작위로 행과 열 번호를 선택
- 게임이 종료 될 때까지 게임 루프를 반복
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
class Tic_Tac_Toe: # 게임판 생성 def __init__(self): # 게임판 초기화 def create_board(self): # 첫 플레이어 선택 def select_first_player(self): # 기호 표시 def mark_spot(self, row, col, player): # 승리 상태 확인 def is_win(self, player): #잔여 빈칸 여부 확인 def is_board_full(self): # 플레이어 변경 def next_player(self, player): # 현재 게임판 상태 출력 def show_board(self): # 게임 시작 def start(self): # 게임 생성 #게임 시작
게임 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import random
class Tic_Tac_Toe:
# 게임판 생성
def __init__(self):
self.board = [] # 클래스 전역에서 사용하기 위해 self 붙임
# 게임판 초기화
def create_board(self):
for i in range(3):
row = [] # 하나의 행 생성
for j in range(3):
row.append('*') # *로 채운 3개의 칸 만들기
self.board.append(row) # self.board 판에 row 넣음
# 첫 플레이어 선택
def select_first_player(self):
if random.randint(0, 1) == 0: # 0, 1 포함해 값 랜덤하게 뽑고 0이 나오면
return 'X' # 컴퓨터(X)가 플레이어
else:
return 'O' # 사용자(O)가 플레이어
# 기호 표시
def mark_spot(self, row, col, player):
self.board[row][col] = player
# 승리 상태 확인
def is_win(self, player):
n = len(self.board) # board가 가리키는 리스트의 원소의 개수 추출
# 행 확인
for i in range(n):
win = True # 승리 상황이라 일단 가정하고 반복할 때마다 각 행에 대해서 다른게 있는지 체크
for j in range(n):
if self.board[i][j] != player: # 세칸이 모두 player의 말과 다르다는 것은 승리 상황이 아니라는 의미
win = False
break # 반복 중단
if win == True:
return win # 승리 상황일 경우 True를 반환
# 열 확인
for i in range(n):
win = True # 승리 상황이라 일단 가정하고 반복할 때마다 각 행에 대해서 다른게 있는지 체크
for j in range(n):
if self.board[j][i] != player: # 열부터 확인할 때에는 i와 j의 위치 바꿈
win = False
break # 반복 중단
if win == True: # 할당 연산자 =와 비교 연산자 == 사용에 유의
return win # 승리 상황일 경우 True를 반환
# 오른쪽 대각선 확인
win = True # 승리 상황이라 일단 가정하고 반복할 때마다 각 행에 대해서 다른게 있는지 체크
for i in range(n):
if self.board[i][i] != player:
win = False
break
if win == True:
return win
# 왼쪽 대각선 확인
win = True # 승리 상황이라 일단 가정하고 반복할 때마다 각 행에 대해서 다른게 있는지 체크
for i in range(n):
if self.board[i][n - i - 1] != player:
win = False
break
if win == True:
return win
return False # 행, 열, 대각선 확인 후에도 승리 상황이 아닌 경우는 False 반환
# 잔여 빈칸 여부 확인
def is_board_full(self):
for row in self.board:
for item in row:
if item == '*':
return False
return True
# 플레이어 변경
def next_player(self, player):
if player == 'O':
return 'X'
else:
return 'O'
# return 'X' if player == 'O' else 'O'
# 현재 게임판 상태 출력
def show_board(self):
for row in self.board:
for item in row:
print(item, end = " ") # 출력할 때 줄바꿈 되지 않게
print()
# 게임 시작
def start(self):
# 새 게임 판 생성
self.create_board()
self.show_board()
# 첫 플레이어 선택
player = self.select_first_player()
# 게임 루프 시작(이벤트가 발생할 때까지 기다림 반복)
while True:
# 다음 플레이어 안내
if player == 'X':
print("컴퓨터 차례입니다.")
else:
print("사용자 차례입니다.")
# 현재 게임 판 상태 출력
self.show_board()
# 사용자 입력 대기, 컴퓨터일 경우 랜덤 위치 반환
if player == 'X':
while True:
row, col = random.randint(1, 3), random.randint(1, 3) # 사용자와 동일한 입력 상황을 맞추기 위해 (1, 3)으로 표기
if self.board[row - 1][col - 1] == '*': # 무작위로 입력 받은 게임판의 좌표 값이 빈칸('*')이면 반복을 중단
break
print("컴퓨터가 행 " + str(row) + ", 열" + str(col) + "을/를 선택했습니다.")
print()
else:
row, col = list(map(int, input("선택할 빈칸의 위치를 입력하세요: ").split())) # 공백 기준으로 문자 분리
print("사용자가 행 " + str(row) + ", 열" + str(col) + "을/를 선택했습니다.")
print()
# row, col 입력 값이 0, 0인 경우 게임 종료
if row == 0 and col ==0:
print("게임을 종료합니다.")
break
# 입력 된 위치 표시
self.mark_spot(row - 1, col - 1, player)
self.show_board()
# 현재 플레이어가 이겼는지 확인
if self.is_win(player) == True: # 승리 상황 메소드 값이 True인지 확인하고
if player == 'X': # 승리자에 맞는 메시지 출
print("컴퓨터가 이겼습니다. 다시 도전하세요")
else:
print("사용자가 이겼습니다. 축하합니다.")
break # 상황 종료
# 게임판 가득참 확인, 빈칸 여부 확인
if self.is_board_full() == True:
print("무승부 입니다. 다시 도전하세요.")
break
# 플레이어 변경 확인
player = self.next_player(player)
# 최종 게임판 출력
print()
self.show_board()
# 게임 생성
TTT = Tic_Tac_Toe()
#게임 시작
TTT.start()
- 게임 판 생성 메소드
- board는 리스트
- []를 통해 리스트 생성
range(0, 3)
범위는 0, 1, 2를 의미
- 첫 플레이어 선택 단계
- 무작위로 둘 중에 하나를 선택
- ex) 0이면 컴퓨터, 1이면 사용자
randint(0, 3)
범위는 0, 1, 2, 3
- 기호 표시 메소드
- 게임 판 상의 행, 열 값에 Player의 말 표시
- []를 통해 리스트 생성
- 잔여 빈칸 여부 확인 메소드
- 한 칸씩 확인한 후, 빈칸을 의미하는 *가 있으면 False 반환
- 플레이어 변경 단계
- 현재 player가 O이면 X를 반환하고 X이면 O를 반환
- 현재 게임 판 상태 출력 메소드
- for문을 활용해 게임 판 상태를 출력
- map 함수
- 리스트 각각의 요소에 함수를 적용해주는 역할
- input으로 입력 받은 위칫 값(리스트 형태)에 (정수로 바꾸는 함수인) int 함수 적용
- 마무리로 어떤 형태로 만들어 주어야 하는지 명시해야 함
- 게임 생성 단계
- Tic_Tac_Toe 객체를 생성
- 게임 시작 단계
- 생성 된 객체가
start()
메소드 호출
- 생성 된 객체가
연습 문제
- 다음 함수에서 0 또는 1의 난수 값이 생성되어 0, X가 반환될 수 있도록 random 모듈의 설명을 참조하여 빈칸에 들어갈 수 있는 표현을 작성하면?
1 2 3 4 5 6 7
import random def select_first_player(self): if ______ == 0: return 'X' else: return 'O'
| 멤버 | 설명 | | ——————– | ——————————– | |
random()
| 0~1 사이의 숫자 중 난수 발생 | |randint(a, b)
| a부터 b 사이의 숫자 중 난수 발생 | |randrange(a, b, c)
| a부터 b 사이의 c의 간격으로 나열된 숫자 중 난수 발생 | |choice(sequence)
| 주어진 항목을 랜덤하게 반환 | |sample(sequence)
| 랜덤하게 여러 개의 원소를 선택 | |shuffle(sequence)
| 시퀀스의 순서를 랜덤하게 섞음 |a.
random.randint(0, 1)
다음 2차원 리스트에서 파란색 원소에 접근하기 위한 표현은?
a.
distance[2][3]
- 다음은 dist라는 이름의 0으로 초기화된 3 X 3 크기의 2차원 리스트 생성하는 코드이다. 빈칸에 공통으로 들어갈 표현은?
1 2 3 4 5 6
dist = ______ for i in range(3): row = ______ for j in range(3): row.append(0) dist.append(row)
a.
[]
또는list()
학습 정리
- 단일 식별자로 순서화 된 값의 집합체를 저장할 수 있는 데이터 타입을 리스트라고 함
- 2차원 리스트는 행 인덱스라는 1차원 리스트를 가리키고 있으며 행 인덱스의 각 원소가 열 인덱스라는 또 다른 1차원 리스트를 포함하는 구조임
- 2차원 리스트의 순회는 각 차원을 순차적으로 순회하기 위한 2개의 반복 구조가 중첩된 구조로 이루어짐