틱택토 게임 구현

python으로 틱택토 게임 구현하기, class 객체 사용하기

틱택토 게임을 구현해봤다. 파이썬은 C/C++에 비해서 문자열 비교가 너무 쉬워서 너무 행복하다.

### 22.10.06
### 틱택토 게임 구현
### class 변환해서 구현

import time


class TicTacToe:
    def __init__(self):
        self.board = {(i + 1): " " for i in range(9)}
        self.turn = 0
        self.player = "B"
        self.markers = {"A": "X", "B": "O"}

    def print_board(self):
        print("")
        for i in range(1, 10):
            print(f"{i}: {self.board[i]} ", end=" ")
            if i % 3 == 0:
                print("")

    #####################
    ### player 전환
    def next_player(self):
        if self.turn % 2 == 0:  # A 차례
            return "A"
        else:
            return "B"

    ######################
    ### input 관리
    def insert_input(self, player, marker):
        input_slot = int(input(f"{player} >> marker({marker}) 표시 위치를 선택하세요(1~9): "))

        while input_slot < 1 or input_slot > 9 or self.board[input_slot] != " ":
            input_slot = int(input(f"{player} >> 적절한 범위와 빈 슬롯의 번호를 다시 입력하세요(1~9): "))

        self.board[input_slot] = marker
        self.turn += 1

    ################
    ### 승패 결정: 승부가 끝났다면 True 반환
    def win_game(self):
        # 세로
        for start in range(1, 4):
            if self.board[start] != " " and self.board[start] == self.board[start + 3] == self.board[start + 6]:
                return True
        # 가로
        for start in range(1, 9, 3):
            if self.board[start] != " " and self.board[start] == self.board[start + 1] == self.board[start + 2]:
                return True

        # 대각선
        if self.board[5] != " " and \
                (self.board[1] == self.board[5] == self.board[9] or self.board[3] == self.board[5] == self.board[7]):
            return True

    ################
    ### 게임을 더 할 건지
    def play_again(self):
        c = input(f"New game? (y/n): ")
        if c == "y":
            self.__init__()
            return True
        else:
            return False

    ################
    ### 메인 게임 실행 함수
    def game(self):
        cont = True # continue game?

        while cont:
            self.print_board()
            self.player = self.next_player()  # self.count 기준으로 다음 순서 정하기

            ### marker 위치 input 받기
            self.insert_input(self.player, self.markers[self.player])

            ### 승패 판정
            res = self.win_game()

            ### 게임 끝, 다음 게임 진행 여부
            if res:
                self.print_board()
                time.sleep(0.5)
                print(f"{self.player} WIN -------------------\n")
                cont = self.play_again()  # 새로운 게임

            if not res and self.turn == 9:  # 무승부 판정
                time.sleep(0.5)
                print(f"TIE GAME -----------\n")
                cont = self.play_again()  # 새로운 게임


ttt = TicTacToe()
ttt.game()

Basic | Set 자료형

파이썬 자료구조 set에 대해 알아보자

집합 (set) 자료구조가 가지는 특징, 함수들은 뭐가 있을까

집합의 특징

  • 중복이 없다
  • 순서가 없다

집합 만들기

  • 딕셔너리와 똑같이 중괄호 {}로 표현
  • 딕셔너리와 다르게 key:value 형식이 없음, 원소만 나열
# 넷 다 같은 집합
set1 = {1, 3, 5}
set2 = set([1, 3, 5]) # set() 함수를 사용해 list to set 변환
set3 = set([5, 3, 1])
set3 = {3, 1, 3, 5} # 집합은 중복 원소 표시하지 않음 

집합 원소 추가/삭제


myset = {1, 2, 3, 4}

# 원소 추가
myset.add(5)

# 여러 개의 원소 한 번에 추가
myset.update([6, 7, 8])

# 원소 제거
myset.remove(5) # 존재하지 않는 원소를 삭제하려고 한다면 에러

# 원소 제거
myset.discard(10) # 존재하지 않는 원소라면 아무것도 하지 않음

집합 연산

  1. 교집합
  2. 합집합
  3. 차집합
  4. XOR (Exclusive or)
# 합집합
union = set1 | set2

# 교집합
intersection = set1 & set2

# 차집합
diff = set1 - set2

# xor
xor = set1^set2

Basic | 특정 데이터 가져오기 (operator.itemgetter)

itemgetter()는 특정 인덱스, key 값을 가진 데이터를 반환

from operator import itemgetter

python의 operator 모듈의 함수 중 하나, operator.itemgetter()

Return a callable object that fetches item from its operand using the operand’s getitem() method.

말이 어렵게 써있지만,

  • list, tuple, dictionary 등의 데이터에서 특정 항목을 가져온다.
  • sorted(), map()에서 활용 가능하도록 호출 가능한 형태를 반환

예시: sorted

학생들의 시험점수를 나타낸 리스트가 있고,

exam = [
    ("Alice", 80),
    ("Jane", 95),
    ("Peter", 72)
]

시험 점수가 높은 학생 순으로 정렬하고 싶다면, sorted()에 들어갈 key 함수에 점수 기준으로 정렬하도록 설정해야 한다.

# 1. 리스트의 각 원소(학생)의 시험점수를 반환하는 함수를 만들고
def get_score(student):
    # (이름, 점수) 형식의 튜플
    return student[1]

# 2. def_score 함수를 key로 넘겨주어 exam 데이터 속의 점수를 기준으로 정렬할 수 있도록 한다.
sorted_students = sorted(exam, key=get_score, reverse=True)

이때 itemgetter()를 사용한다면 get_socre 함수를 만들지 않고, 특정 원소를 바로 반환받을 수 있다.

# itemgetter(1): 1번째 원소를 반환
sorted_students = sorted(exam, key=itemgetter(1), reverse=True)

작동 원리

itemgetter(1)("ABCDEFG")
### >>> 'B'

itemgetter(0, 2)("ABCDEFG") # 여러 개의 값이 선택되면 tuple 형태로 반환
### >>> ('A', 'C')

f = itemgetter(2) 라면 f(r) 호출 후 r[2] 반환하는 형태와 같다.

itemgetter()의 구현은 다음과 같다. 근데 알 필요가 없죠

def itemgetter(*items):
    if len(items) == 1:
        item = items[0]
        def g(obj):
            return obj[item]
    else:
        def g(obj):
            return tuple(obj[item] for item in items)
    return g

활용: map

map 함수는 iterable한 모든 항목에 지정한 funciton을 적용한 후 그 결과를 반환 (정확히는 결과를 내는 iterator를 반환한다, 반환형도 list가 아니라 map object)

inventory = [('apple', 5), ('banana', 10), ('orange', 2), ('kiwi', 1)]

# map: inventory라는 리스트에 itemgetter(0) 함수를 동일하게 적용
# itemgetter: inventory라는 리스트에서 0번째 item을 가져옴
list(map(itemgetter(0), inventory))

### >>> ['apple', 'banana', 'orange', 'kiwi']

활용: dictionary key

itemgetter()의 매개변수로 dictionary 자료형의 key 값을 넣어줄 수 있다.

students = [
    {'name': 'Alice', 'age': 19, 'score': 80},
    {'name': 'Jane', 'age': 17, 'score': 95},
    {'name': 'Peter', 'age': 16, 'score': 72},
]

sorted(students, key=itemgetter('age'))
### >>> [{'name': 'Peter', 'age': 16, 'score': 72},
###     {'name': 'Jane', 'age': 17, 'score': 95},
###     {'name': 'Alice', 'age': 19, 'score': 80}]


Reference

  • https://docs.python.org/3/library/operator.html#operator.lt
  • https://wikidocs.net/109327

Basic | Python 문자열 함수

파이썬 문자열에 자주 사용하는 함수

파이선 문자열을 다룰 때 쓰는 대표 함수들을 알아보자. (리스트 함수 포함 ㅎ)

목차

  1. startswith()
  2. split()
  3. lower()
  4. replace()

startswith()

string.startswith("<특정문자열>")

문자열이 특정 문자열로 시작하는지 검사, True/False 반환

s = "I'm banana"

print(s.startswith("I")) # True

if s.startswith("I"):
  print("문자열 s는 I로 시작함")

if s.startswith("I'm"):
  print("문자열 s는 I'm로 시작함")

split()

string.split("<기준 문자>")

문자열을 특정 기준 문자로 쪼개서 리스트로 반환

s = "I'm banana"

# split() 함수 안에 기준 문자가 없을 때: 공백 기준으로 문자열을 split
print(s.split()) 
# >>> ["I'm", "banana"]

print(s.split("'")) 
# >>> ["I", "'m banana"]

split() vs. split(‘ ‘)

split(): 공백 전부 제거
split(' '): 공백 기준으로 모두 split, 빈 문자열이 반환됨

numbers = "  1 2 3  "
print(numbers.split())
# >>> ['1', '2', '3']

print(numbers.split(' '))
# >>> ['', '', '1', '', '2', '', '3', '', '' ]

lower()

문자열의 대소문자 변환

s = "I'm banana"

print(s.lower())
# >>> i'm banana

print(s.upper())
# >>> I'M BANANA

주의!
s.lower()는 문자열을 직접 수정하지 않고 새로운 값 생성.

s = "I'm banana"
s.upper()
print(s)
# >>> I'm banana

s = s.upper() # 꼭 반환값을 새로운 문자열 변수나 기존 변수에 넣어주도록 하자
print(s)
# >>> I'M BANANA

replace()

문자열.replace("기존 문자열", "대체할 문자열")

특정 문자열로 대체

s = "I'm banana"

print(s.replace("I'm", "I am"))
# >>> I am banana

# 대체할 문자열에 아무것도 입력하지 않으면 문자 제거로 활용할 수 있음
print(s.replace("'", ""))
# >>> Im banana

주의
마찬가지로 replace()는 문자열을 직접 수정하지 않고 새로운 값을 생성한다.

Baisc | List Comprehension 문법

리스트로 리스트 만들기

리스트 생성을 한 줄로 하기


List Comprehension 이란

리스트를 생성할 때 짧게 한 줄로 만드는 문법

[<원소의 최종 형태> for <변수> in <순회 가능한 , 리스트 >]

예시: list로 list 만들기

이미 존재하는 list의 값을 변형하여 새로운 list를 만드려고 할 때의 예시

# 요일의 맨 첫 글자만 따서 새로운 리스트를 만드려고 할 때
days = ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"]

new_days = []
for day in days:
  new_days.append(day[0])

print(new_days)
# >>> ['M', 'T', 'W', 'T', 'F', 'S', 'S']

위와 같이 for문으로 순회하면서 만들어내야 하는 리스트를 한 줄로 짧게 만들 수 있게 하는 문법이 리스트 컴프리헨션이다.

# list comprehension 
days = ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"]

new_days = [day[0] for day in days]
# >>> ['M', 'T', 'W', 'T', 'F', 'S', 'S']

예시: range() 활용

range() 함수는 특정 범위 값의 정수 리스트를 반환하므로 아래와 같이 활용할 수 있다.

# 0부터 20미만 짝수 list 만들기
even_nums = [i*2 for i in range(10)]

# >>> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

예시: if문 활용

# 0부터 20미만 짝수 list 만들기
even_nums = [i for i in range(20) if i%2==0]

# >>> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]


Pandas | Pandas 심화 (데이터 검색, 조건)

pandas 자주 쓰는 함수 정복하기: query(), apply(), groupby()

목차

  1. 데이터 조건 검색하기(query, str.contains)
  2. 데이터에 함수 적용하기(apply, lambda)
  3. 데이터 조건으로 묶기(groupby, aggregate)
  4. 멀티인덱스
  5. Pivot Table


import pandas as pd

데이터: 조건 검색

1. masking 연산

numpy와 마찬가지로 마스킹 연산 (True, False 반환)으로 원하는 row만 뽑을 수 있다.

df = pd.DataFrame(np.random.rand(5, 2), columns=["A","B"])

print(df[df["A"] < 0.5])
print(df[(df["A"] < 0.5) & (df["B"] > 0.3)] # 조건 여러개

making 연산 실행 예시

2. query() 함수 사용

pandas.DataFrame.query 공식문서

df = pd.DataFrame(np.random.rand(5, 2), columns=["A","B"])

df.query("A<0.5")
df.query("A<0.5 and B>0.3") # 조건 여러 개: query 문자열 안에 and or 사용

3. 문자열 검색

# 1. # Cat을 포함한 문자열 찾기
df["Animal"].str.contains("Cat") 

# 2. # Cat과 완전히 매칭되는 문자열 찾기
df.Animal.str.match("Cat") # 특징: match() 안에 정규표현식 사용 가능

# 3. 2와 같음
df["Animal"] == "Cat"

데이터: 함수 처리

dataframe의 데이터에 함수 적용하기

df = pd.DataFrame(np.arange(5), columns="Num")

def square(x): # 제곱하는 함수
  return x**2

# Num 열의 데이터들을 제곱하여 새로운 열 Square을 추가해보자
# 1. 만들어둔 square 함수 사용
df["Square"] = df["Num"].apply(square)

# 2. 람다식 사용
df["Square"] = df["Num"].apply(lambda x:x**2)

데이터: 그룹으로 묶기 (조건부 집계)

1. groupby()

특정 열을 기준으로 데이터를 집계

df = pd.DataFrame({"key": list("ABCABC"), "col1": [1, 2,3,1,4,3,], "col2": np.random.randint(0, 6, 6)})

df 출력

위와 같은 데이터가 있을 때 key값을 기준으로 묶어 합계를 내고 싶다면.

# key 열을 기준으로 묶음 -> 합계
df.groupby("key").sum()

# 두 개의 열을 기준으로 묶음 -> 평균
df.groupby(["key", "col1"]).mean()

결과

2. aggregate()

groupby를 통해 집계를 한 번에 계산하는 함수

df.groupby(''' /기준열/ ''').aggregate(''' /list or dict/ ''')

# 1. 리스트 형태로 각 열에 공통적으로 적용
df.groupby("key").aggregate([min, np.median, max])

# 2. 딕셔너리 형태로 각 열마다 다른 집계 수행
df.groupby("key").aggregate({"data1":min, "data2":sum})

각 열의 min, median, max 값 한 번에 계산

3. filter()

groupby를 통해 그룹 속성을 기준으로 데이터 필터링

  • groupby와 함께 사용하여 특정 기준을 만족하는 데이터 row만 출력
  • column 별 계산
def filter_mean(x):
    return x["col2"].mean() > 3

df.groupby("key").filter(filter_mean)

결과

col2의 mean이 3초과인 데이터 ()= key가 C인) row(2, 5)만 필터링

4. apply()

groupby를 통해서 묶은 데이터에 함수 적용

  • apply를 groupby와 함께 사용할 수 있다.
  • column 별 계산

멀티인덱스

인덱스가 중복될 때, 계층적으로 만들어진다.

  • row, col 모두 계층적으로 생성
  • iloc, loc 사용 가능
df = pd.DataFrame(np.random.randn(10, 4), index=[list("AABBBAAABB"), [1, 2, 1, 1, 2, 1, 2, 2, 1, 1, ]], columns=[["col1", "col1", "col2", "col2"], [1, 2, 3, 4]])

df 출력 결과

# 멀티인덱스 접근 예시
df["col1"][1]

Pivot Table

데이터에서 필요한 자료만 뽑아서 새롭게 요약

  • index: 행 인덱스로 들어갈 key
  • column: 열 인덱스로 라벨링될 값
  • value: 분석할 데이터 (값)
# 일차별로, (row)
# 사람들의 평균 나이를 구함 (value)
# 성별로 라벨링 (col)
tb = df.pivot_table(
  index="일차", columns="성별", values="나이",
  aggfunc=np.mean # value 값을 어떻게 처리할건지
)

print(tb)

결과

시각화 | Matplotlib 기초

matplotlib 라이브러리 기본 사용법

matplotlib: 파이썬에서 데이터를 그래프나 차트로 시각화할 수 있는 라이브러리


import matplotlib.pyplot as plt

그래프 그리기

두 가지 방법이 있다.

  1. plt.plot()
  2. plt.subplots()

1. plt.plot()

state machine interface 방식

선(또는 점) 그래프

# plt.plot(x, y)
x = list(map(int, range(5))) # [0, 1, 2, 3, 4]
y = list(map(int, range(4, 9))) # [4, 5, 6, 7, 8]

plt.plot(x, y)

그래프 결과

해상도, 타이틀, 범주 등

### fig: 크기 조절, dpi: 해상도 설정(default: 100)
plt.figure(figsize=(4, 4), dpi=200) 

### plot 라벨 범주 표현하기
plt.plot(x, y, label="x-y")
plt.plot(x, z, label="x-z")
plt.legend(loc="lower right") 

### 그래프 타이틀
plt.title("[Example Plot]")

### x, y 라벨
plt.xlabel("X")
plt.ylabel("Y")

# 좌표평면 범위 조절
plt.ylim([0, 10])

# 눈금 추가/삭제 (스타일마다 default가 다름)
plt.grid()

범주, 타이틀, 라벨 예시

Line Style

x, y = np.array(x), np.array(y)

plt.plot(x, y,  linestyle="-", marker="o")
plt.plot(x, y+1, linestyle="--", marker="^")
plt.plot(x, y+2, linestyle="-.", marker="*")
plt.plot(x, y+3, linestyle=":")

line style, marker 예시

2. plt.subplots()

object oriented interface 방식

# fig, ax = plt.subplots()
x = list(map(int, range(5))) # [0, 1, 2, 3, 4]
y = list(map(int, range(4, 9))) # [4, 5, 6, 7, 8]
z = list(map(int, range(2, 7))) # [2, 3, 4, 5, 6]

fig, ax = plt.subplots()
ax.plot(x, y)
  • fig: 전체 figure, 도화지라고 생각
  • ax: 그래프 객체
  • 하나의 fig에 여러 개의 그래프를 그릴 수 있다.
  • 하나의 ax에 여러 개의 선을 나타낼 수 있다.

subplots(nrow, ncols) 그리드 지정

#### plt.subplots(nrows=1, ncols=1) # int, default:1

fig, axes = plt.subplots(2, 1) # 2행 1열 그리드 (위 아래)

# 그래프 그리기
axes[0].plot(x, y)
axes[1].plot(x, z)

그리드 지정 그래프

subplots() 그래프 예시

plt.plot()으로 그리는 것과 동일하게 여러가지 옵션을 줄 수 있다.

x = np.arange(10)
fig, ax = plt.subplots()

ax.plot(
    x, x, label='y=x',
    marker='o',
    color='blue',
    linestyle=':'
)
ax.plot(
    x, x**2, label='y=x^2',
    marker='^',
    color='red',
    linestyle='--'
)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.legend(
    loc='upper left',
    shadow=True,
    fancybox=True,
    borderpad=2
)

subplots() 옵션 예시


그래프 테마

단순히 그래프 선 색깔을 바꾸는 것 말고도 다양한 테마를 제공한다.

matplotlib stylesheet에서 다양한 스타일을 고를 수 있다.

import matplotlib.pyplot as plt

# 'seaborn' 스타일 사용 선언
plt.style.use("ggplot")

나는 ggplot이나 seaborn이 제일 예쁘고 눈에 잘 들어오는 듯

ggplot 스타일시트


다른 그래프: Scatter, Bar, Histogram

1. Scatter 그래프

plt.figure(figsize=(4, 4), dpi=100) # fig 크기 조절, 해상도 설정

plt.scatter(x, y, alpha=0.8, marker="*") # alpha: 투명도

산점도 그래프 예시

pandas.DataFrame 산점도 그래프

import pandas as pd

df = pd.DataFrame(np.random.rand(100, 2)); 
print(df)

plt.scatter(df[0], df[1])

산점도 그래프 예시2

2. Bar 그래프

plt.bar(x=np.arange(5), height=3*x-1)

막대 그래프 예시

3. Histogram 그래프

plt.hist([1, 1, 1, 2, 3, 4, 2, 5])
# 값들에 대한 통계 (빈도수)

히스토그램 예시


Reference

  • 다양한 matplotlib 예제를 보고 싶다면: example

Baisc | Python lambda

파이썬 람다식 배우기

람다식은 맨날 봐도봐도 익숙하지 않고 문법도 헷갈린다.
이참에 정리를 해야지.

lambda = 한 줄 함수

lambda 매개변수 : 표현식

labmda 활용 예시

1. 두 수를 더하는 함수

def _adder(x, y):
  return x+y

adder = (lambda x,y: x+y)

# 사용 예시
a = _adder(1, 3)
b = adder(1, 3)
# a = b

2. 제곱 함수

def _square(x):
  return x*x

square = (lambda x: x*x)

3. if-else를 포함한 함수

# 매개변수로 받은 문자열의 첫 글자를 반환, 빈 문자열인 경우 빈 문자열 반환
def _first_letter(s):
  return s[0] if s else ""

first_letter = (lambda s: s[0] if s else "")

생략이 많다.

  • 함수 이름 필요 없음
  • return 키워드 필요 없음

매개변수 부분이 입력(in) 표현식 부분이 반환(out)

map vs. list comprehension

map()은 데이터 구조의 각 원소들에 동일한 함수를 적용하여 새로운 데이터를 만든다.

# 아래의 두 줄은 유사한 연산을 수행
>>> [func(x) for x in data]
>>> map(func, data)

차이점

연산 진행 시점

map()은 데이터가 필요해질 때 연산 수행

filter()

Pagination