티스토리 뷰

수업 노트/python

tic-tac-toe(오목 말고 삼목) 만들기

오리지날초이 2023. 11. 12. 09:58

 

[step 1] player 객체 선언과 TicTacToe 객체 선언

# player.py

import math
import random

class Player():
    def __init__(self, letter):
        self.letter = letter

    def get_move(self, game):
        pass

class RandomComputerPlayer(Player):
    def __init__(self, letter):
        super().__init__(letter)
    
    def get_move(self, game):
        pass


class HumanPlayer(Player):
    def __init__(self, letter):
        super().__init__(letter)

    def get_move(self, game):
        pass
# game.py

class TicTacToe():
    def __init__(self):
        self.board = self.make_board()
        self.current_winner = None

    @staticmethod
    def make_board():
        return [' ' for _ in range(9)]

    def print_board(self):
        for row in [self.board[i*3:(i+1) * 3] for i in range(3)]:
            print('| ' + ' | '.join(row) + ' |')

    @staticmethod
    def print_board_nums():
        # 0 | 1 | 2
        number_board = [[str(i) for i in range(j*3, (j+1)*3)] for j in range(3)]
        for row in number_board:
            print('| ' + ' | '.join(row) + ' |')

 

[step 1-easy] TicTacToe 쉬운 버전

# easy 1

class TicTacToe():
    def __init__(self):
        self.board = [' ',' ',' ',' ',' ',' ',' ',' ',' ']
        self.current_winner = None
    
    def print_board(self):
        row_init = [self.board[0:3]]
        # print(row_init)
        for row in row_init:
            print('| ' + ' | '.join(row) + ' |')

        row_init = [self.board[3:6]]
        for row in row_init:
            print('| ' + ' | '.join(row) + ' |')

        row_init = [self.board[6:9]]
        for row in row_init:
            print('| ' + ' | '.join(row) + ' |')


# esay 2

class TicTacToe():
    def __init__(self):
        self.board = [' ',' ',' ',' ',' ',' ',' ',' ',' ']
        self.current_winner = None
    
    def print_board(self):

        i=0
        for row in [self.board[i:i+3]]:
            print('| ' + ' | '.join(row) + ' |')
            i=i+3

        for row in [self.board[i:i+3]]:
            print('| ' + ' | '.join(row) + ' |')
            i=i+3

        for row in [self.board[i:i+3]]:
            print('| ' + ' | '.join(row) + ' |')
            i=i+3

# esay 3

class TicTacToe():
    def __init__(self):
        self.board = [' ',' ',' ',' ',' ',' ',' ',' ',' ']
        self.current_winner = None
    
    def print_board(self):

        i=0
        for row in [self.board[i:i+3] for i in range(3)]:
            print('| ' + ' | '.join(row) + ' |')
            i=i+3

 

 

[step 1-check] TicTacToe 의 method 동작 확인

# game.py 에 이어서

TTT=TicTacToe()

TTT.print_board()
print()
TTT.print_board_nums()
#실행결과

|   |   |   |
|   |   |   |
|   |   |   |

| 0 | 1 | 2 |
| 3 | 4 | 5 |
| 6 | 7 | 8 |

 

[step 2] 각 player 의 착수 구역 get_move() 구현하기

# game.py
class TicTacToe():
    ...    
    def available_moves(self):
        return [i for i, x in enumerate(self.board) if x == " "]
        
    # >>> t = [1, 5, 7, 33, 39, 52]
    # >>> for p in enumerate(t):
    # ...     print(p)
    # ... 
    # (0, 1)
    # (1, 5)
    # (2, 7)
    # (3, 33)
    # (4, 39)
    # (5, 52)

    # >>> for i, v in enumerate(t):
    # ...     print("index : {}, value: {}".format(i,v))
    # ... 
    # index : 0, value: 1
    # index : 1, value: 5
    # index : 2, value: 7
    # index : 3, value: 33
    # index : 4, value: 39
    # index : 5, value: 52
    
    
# player.py
class RandomComputerPlayer(Player):
...
    def get_move(self, game):
        box = random.choice(game.available_moves())
        return box
        
class HumanPlayer(Player):
...
    def get_move(self, game):
        valid_box = False
        value = None
        while not valid_box:
            box = input(self.letter + '\'s turn. Input move (0-9): ')
            try:
                value = int(box)
                if value not in game.available_moves():
                    raise ValueError
                valid_box = True
            except ValueError:
                print('Invalid box. Try again.')
        return value

 

 

[step 3] game.py 에 play 함수 구현, TicTacToe 클래스에 empty_box 관련 함수 구현

# game.py

class TicTacToe():
...
	def empty_box(self):
		return ' ' in self.board	# return true or false

	def num_empty_box(self):
		return self.board.count(' ')


def play(game, x_player, o_player, print_game=True):

    if print_game:
        game.print_board_nums()

    letter = 'X' # first turn letter
    while game.empty_box(): 
        pass	 # todo

        
# game.py 에서 동작확인
...
TTT=TicTacToe()
print(TTT.empty_box())
print(TTT.num_empty_box())

# Terminal
True
9

 

[step 4] game.py 의 play 함수 추가 구현, TicTacToe 클래스에 make_move 함수 구현

# game.py

def play(game, x_player, o_player, print_game=True):
...
	while game.empty_box():
		if letter == 'O':
			box = o_player.get_move(game)
		else:
			box = x_player.get_move(game)

		if game.make_move(box, letter):
			if print_game:
				print(letter + ' makes a move to box {}'.format(box))
				game.print_board()
				print('')

		letter = 'O' if letter == 'X' else 'X'  # switches player            
            
# game.py
class TicTacToe():
...
	def make_move(self, box, letter):
		if self.board[box] == ' ':
			self.board[box] = letter
			return True
		return False

 

 

[step 5] winner 관련 추가 구현

# game.py

class TicTacToe():
...
 
	def make_move(self, box, letter):
		if self.board[box] == ' ':
			self.board[box] = letter

			if self.winner(box, letter): # todo 
				self.current_winner = letter
			return True
		return False
        
      
...
def play(game, x_player, o_player, print_game=True):
...
	while game.empty_box():
            if letter == 'O':
                box = o_player.get_move(game)
            else:
                box = x_player.get_move(game)

            if game.make_move(box, letter):

                if print_game:
                    print(letter + ' makes a move to box {}'.format(box))
                    game.print_board()
                    print('')

                if game.current_winner:
                    if print_game:
                        print(letter + ' wins!')
                    return letter  # ends the loop and exits the game

                letter = 'O' if letter == 'X' else 'X'  # switches player
                
        if print_game:
            print('It\'s a tie!')

 

[step 6] win 판정 세부 로직 구현

# game.py
import math


class TicTacToe():
...	
    def winner(self, box, letter):
        # check the row
        row_index = math.floor(box / 3)
        # math.florr : Round numbers down to the nearest integer
        # print(math.floor(0.6)) -> 0
        # print(math.floor(1.4)) -> 1
        row = self.board[row_index*3:(row_index+1)*3]

        if all([s == letter for s in row]):
            return True
        
        col_index = box % 3
        column = [self.board[col_index+i*3] for i in range(3)]
        
        if all([s == letter for s in column]):
            return True
        
        if box % 2 == 0:
            diagonal1 = [self.board[i] for i in [0, 4, 8]]
            if all([s == letter for s in diagonal1]):
                return True
            
            diagonal2 = [self.board[i] for i in [2, 4, 6]]
            if all([s == letter for s in diagonal2]):
                return True
            
        return False

 

 

[step 7] main 실행 구현

# game.py

import math
from player import HumanPlayer, RandomComputerPlayer

...

if __name__ == '__main__':
    x_player = HumanPlayer('X')
    o_player = RandomComputerPlayer('O')
    t = TicTacToe()
    play(t, x_player, o_player, print_game=True)

 

 

[step 8 & final] 실행 지연 추가

# game.py

import time
...

def play(game, x_player, o_player, print_game=True):
...

    while game.empty_box():
	...

    	letter = 'O' if letter == 'X' else 'X'  # switches player      

    	time.sleep(.8)
        
	...

 

 

[참고자료]

 

https://mingrammer.com/underscore-in-python/

 

파이썬 언더스코어(_)에 대하여

파이썬에서 언더스코어(underscore, _)는 특별하다. 타 언어에서 언더스코어(_)는 단지 스네이크 표기법의 변수나 함수명을 위해서만 사용되어지

mingrammer.com

 

https://blockdmask.tistory.com/468

 

[python] 파이썬 join 함수 정리 및 예제 (문자열 합치기)

안녕하세요. BlockDMask입니다. 오늘은 파이썬에서 리스트를 문자열로 일정하게 합쳐주는 join 함수에 대해서 알아보려고 합니다. join 함수는 문자열을 다룰 때 유용하게 사용할 수 있는 함수이니

blockdmask.tistory.com

 

https://www.daleseo.com/python-class-methods-vs-static-methods/

 

[파이썬] 정적(static) 메서드와 클래스(class) 메서드

Engineering Blog by Dale Seo

www.daleseo.com

 

https://devpouch.tistory.com/74

 

[Python] 파이썬 enumerate 함수 사용법/예제

enumerate함수 반복문을 사용할때 리스트의 순서값, 즉 인덱스의 정보가 필요한 경우가 있습니다. enumerate함수는 리스트의 원소에 순서값을 부여해주는 함수입니다. 사용 예제는 아래와 같습니다.

devpouch.tistory.com

 

 

 

https://www.youtube.com/watch?v=8ext9G7xspg&t=2153s

 

 

https://github.com/kying18/tic-tac-toe

 

GitHub - kying18/tic-tac-toe: Tic-tac-toe AI using minimax

Tic-tac-toe AI using minimax. Contribute to kying18/tic-tac-toe development by creating an account on GitHub.

github.com

 

 

 

 

 

728x90
반응형

'수업 노트 > python' 카테고리의 다른 글

wsl, vim 을 활용한 python 개발 환경 세팅  (0) 2023.12.09
파이썬 개발환경 구축  (0) 2023.11.04
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
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
글 보관함