2016년 5월 14일 토요일

Pygame 틱택토 게임 (Tic-Tac-Toe) (2)

import random, pygame, sys
from pygame.locals import *
FPS = 30 # frames per second, the general speed of the program
WINDOWWIDTH = 340 # size of window's width in pixels
WINDOWHEIGHT = 340 # size of windows' height in pixels
BOXSIZE = 100 # size of box height & width in pixels
GAPSIZE = 10 # size of gap between boxes in pixels
BOARDWIDTH = 3 # number of columns of icons
BOARDHEIGHT = 3 # number of rows of icons
# Colorset
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (100, 100, 100)
LIGHTBLUE = (153, 204, 255)
BGCOLOR = WHITE
BOXCOLOR = BLACK
HIGHLIGHTCOLOR = GRAY
LINECOLOR = WHITE
def main():
global FPSCLOCK, DISPLAYSURF
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
mousex = 0 # used to store x coordinate of mouse event
mousey = 0 # used to store y coordinate of mouse event
pygame.display.set_caption('TicTacToe - harang97')
mainBoard = [[None,None,None],[None,None,None],[None,None,None]]
playerTurn = 'X'
firstSelection = None # stores the (x, y) of the first box clicked.
DISPLAYSURF.fill(BGCOLOR)
drawBoard(mainBoard)
Message = 'None'
while True: # main game loop
mouseClicked = False
DISPLAYSURF.fill(BGCOLOR)
drawBoard(mainBoard)
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
elif event.type == MOUSEMOTION:
mousex, mousey = event.pos
elif event.type == MOUSEBUTTONUP:
mousex, mousey = event.pos
mouseClicked = True
boxx, boxy = getBoxAtPixel(mousex, mousey)
if boxx != None and boxy != None:
# the mouse is currently over a box.
if mainBoard[boxx][boxy] == None:
drawHighlightBox(boxx, boxy)
if mainBoard[boxx][boxy] == None and mouseClicked:
mainBoard[boxx][boxy] = playerTurn # set the box as "filled"
drawXO(playerTurn, boxx, boxy)
if playerTurn == 'X':
playerTurn = 'O'
else: playerTurn = 'X'
# Algorithm that check the game is over
if hasWon(mainBoard):
if playerTurn == 'X': Message = 'Player 1 Wins!'
else: Message = 'Player 2 Wins!'
popMessage(Message)
mainBoard = [[None,None,None],[None,None,None],[None,None,None]]
playerTurn = 'X'
elif hasDraw(mainBoard):
Message = '   Draw!    '
popMessage(Message)
mainBoard = [[None,None,None],[None,None,None],[None,None,None]]
playerTurn = 'X'
# Redraw the screen and wait a clock tick.
pygame.display.update()
FPSCLOCK.tick(FPS)
def popMessage(Message):
font = pygame.font.Font('font/nanum.ttf', 32)
textSurface = font.render(Message, True, BLACK, LIGHTBLUE)
textRect = textSurface.get_rect()
textRect.center = (170, 85)
DISPLAYSURF.blit(textSurface, textRect)
pygame.display.update()
pygame.time.wait(2000)
def getBoxAtPixel(x, y):
# Draw Box on display surface
for boxx in range(BOARDWIDTH):
for boxy in range(BOARDHEIGHT):
left, top = leftTopCoordsOfBox(boxx, boxy)
boxRect = pygame.Rect(left, top, BOXSIZE, BOXSIZE)
if boxRect.collidepoint(x, y):
return (boxx, boxy)
return (None, None)
def drawBoard(board):
# Draws all of the boxes in their covered or revealed state.
for boxx in range(BOARDWIDTH):
for boxy in range(BOARDHEIGHT):
left, top = leftTopCoordsOfBox(boxx, boxy)
if board[boxx][boxy] == None:
pygame.draw.rect(DISPLAYSURF, BOXCOLOR, (left, top, BOXSIZE, BOXSIZE))
else:
pygame.draw.rect(DISPLAYSURF, BOXCOLOR, (left, top, BOXSIZE, BOXSIZE))
drawXO(board[boxx][boxy], boxx, boxy)
def leftTopCoordsOfBox(boxx, boxy):
# Convert board coordinates to pixel coordinates
left = boxx* (BOXSIZE + GAPSIZE) + GAPSIZE
top = boxy * (BOXSIZE + GAPSIZE)  + GAPSIZE
return (left, top)
def drawHighlightBox(boxx, boxy):
left, top = leftTopCoordsOfBox(boxx, boxy)
pygame.draw.rect(DISPLAYSURF, HIGHLIGHTCOLOR, (left , top , BOXSIZE , BOXSIZE))
def drawXO(playerTurn, boxx, boxy):
left, top = leftTopCoordsOfBox(boxx, boxy)
if playerTurn == 'X':
pygame.draw.line(DISPLAYSURF, LINECOLOR, (left + 3, top + 3), (left + BOXSIZE - 3, top + BOXSIZE - 3), 4)
pygame.draw.line(DISPLAYSURF, LINECOLOR, (left + BOXSIZE - 3, top + 3), (left + 3, top + BOXSIZE - 3), 4)
else:
HALF = int(BOXSIZE / 2)
pygame.draw.circle(DISPLAYSURF, LINECOLOR, (left + HALF, top + HALF), HALF - 3, 4)
def hasWon(board):
# Returns True if player 1 or 2 wins
for xrow in board: # horizontal
if xrow[0] != None and xrow[0] == xrow[1] and xrow[1] == xrow[2]:
return True
for i in range(3): # vertical
if board[0][i] != None and board[0][i] == board[1][i] and board[1][i] == board[2][i]:
return True
if board[0][0] != None and board[0][0] == board[1][1] and board[1][1] == board[2][2]: # diagnol 1
return True
if board[2][0] != None and board[2][0] == board[1][1] and board[1][1] == board[0][2]: # diagnol 2
return True
return False # no winner
def hasDraw(board):
# Returns True if all the boxes have been filled
for i in board:
if None in i:
return False
return True
if __name__ == '__main__':
main()

(1)에서 구현해야 할 점 구현 완료!

부족한 점
- 승리시 표시하는 배경 박스를 그린 후 배경색이 없는 텍스트를 그리려 했으나, 결국 텍스트 배경색 이용
- 승리&무승부시 텍스트가 떠 있는 상태에서 클릭을 할 경우 다음 판에 반영된다.

보완점
- 텍스트를 사용하는 것이 아니라 팝업 텍스트 자체를 png로 저장한 후 불러오는 방법 고려

알게된 점
- pygame.display.update() 함수를 사용하는 이유는 화면 상에서 그래픽 변경 사항을 저장한 후 실제로 우리가 볼 수 있도록 하기 위해서이다.

Pygame 틱택토 게임 (Tic Tac Toe) 만들기 (1)


import random, pygame, sys
from pygame.locals import *

FPS = 30 # frames per second, the general speed of the program
WINDOWWIDTH = 340 # size of window's width in pixels
WINDOWHEIGHT = 340 # size of windows' height in pixels
BOXSIZE = 100 # size of box height & width in pixels
GAPSIZE = 10 # size of gap between boxes in pixels
BOARDWIDTH = 3 # number of columns of icons
BOARDHEIGHT = 3 # number of rows of icons

# Colorset
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (100, 100, 100)

BGCOLOR = WHITE
BOXCOLOR = BLACK
HIGHLIGHTCOLOR = GRAY
LINECOLOR = WHITE

O = 'O'
X = 'X'

def main():
 global FPSCLOCK, DISPLAYSURF
 pygame.init()
 FPSCLOCK = pygame.time.Clock()
 DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))

 mousex = 0 # used to store x coordinate of mouse event
 mousey = 0 # used to store y coordinate of mouse event
 pygame.display.set_caption('TicTacToe - harang97')

 mainBoard = [[None,None,None],[None,None,None],[None,None,None]]
 playerTurn = 'X'

 firstSelection = None # stores the (x, y) of the first box clicked.

 DISPLAYSURF.fill(BGCOLOR)
 drawBoard(mainBoard)

 while True: # main game loop
  mouseClicked = False

  DISPLAYSURF.fill(BGCOLOR)
  drawBoard(mainBoard)

  for event in pygame.event.get():
   if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
    pygame.quit()
    sys.exit()
   elif event.type == MOUSEMOTION:
    mousex, mousey = event.pos
   elif event.type == MOUSEBUTTONUP:
    mousex, mousey = event.pos
    mouseClicked = True

  boxx, boxy = getBoxAtPixel(mousex, mousey)
  if boxx != None and boxy != None:
   # the mouse is currently over a box.
   if mainBoard[boxx][boxy] == None:
    drawHighlightBox(boxx, boxy)
   if mainBoard[boxx][boxy] == None and mouseClicked:
    mainBoard[boxx][boxy] = playerTurn # set the box as "filled"
    drawXO(playerTurn, boxx, boxy)
    if playerTurn == 'X':
     playerTurn = 'O'
    else: playerTurn = 'X'

    # Algorithm that check the game is over
    if hasWon(mainBoard):
     pass
    if hasDraw(mainBoard):
     pass
    # -----------------------------
  # Redraw the screen and wait a clock tick.
  pygame.display.update()
  FPSCLOCK.tick(FPS)


def getBoxAtPixel(x, y):
 # Draw Box on display surface
 for boxx in range(BOARDWIDTH):
  for boxy in range(BOARDHEIGHT):
   left, top = leftTopCoordsOfBox(boxx, boxy)
   boxRect = pygame.Rect(left, top, BOXSIZE, BOXSIZE)
   if boxRect.collidepoint(x, y):
    return (boxx, boxy)
 return (None, None)


def drawBoard(board):
 # Draws all of the boxes in their covered or revealed state.
 for boxx in range(BOARDWIDTH):
  for boxy in range(BOARDHEIGHT):
   left, top = leftTopCoordsOfBox(boxx, boxy)
   if board[boxx][boxy] == None:
    pygame.draw.rect(DISPLAYSURF, BOXCOLOR, (left, top, BOXSIZE, BOXSIZE))
   else:
    pygame.draw.rect(DISPLAYSURF, BOXCOLOR, (left, top, BOXSIZE, BOXSIZE))
    drawXO(board[boxx][boxy], boxx, boxy)


def leftTopCoordsOfBox(boxx, boxy):
 # Convert board coordinates to pixel coordinates
 left = boxx* (BOXSIZE + GAPSIZE) + GAPSIZE
 top = boxy * (BOXSIZE + GAPSIZE)  + GAPSIZE
 return (left, top)


def drawHighlightBox(boxx, boxy):
 left, top = leftTopCoordsOfBox(boxx, boxy)
 pygame.draw.rect(DISPLAYSURF, HIGHLIGHTCOLOR, (left , top , BOXSIZE , BOXSIZE))

def drawXO(playerTurn, boxx, boxy):
 left, top = leftTopCoordsOfBox(boxx, boxy)
 if playerTurn == 'X':
  pygame.draw.line(DISPLAYSURF, LINECOLOR, (left + 3, top + 3), (left + BOXSIZE - 3, top + BOXSIZE - 3), 4)
  pygame.draw.line(DISPLAYSURF, LINECOLOR, (left + BOXSIZE - 3, top + 3), (left + 3, top + BOXSIZE - 3), 4)
 else:
  HALF = int(BOXSIZE / 2)
  pygame.draw.circle(DISPLAYSURF, LINECOLOR, (left + HALF, top + HALF), HALF - 3, 4)

def hasWon(board):
 # Returns True if player 1 or 2 wins
 return True


def hasDraw(board):
 # Returns True if all the boxes have been filled
 for i in board:
  if None in i:
   return False
 return True

if __name__ == '__main__':
 main()

현재 완료
- 기본 그래픽 바탕
- 마우스 커서가 빈 공간에 올라올 경우 하이라이트
- 클릭시 차례에 따라 X 또는 O가 바탕에 남음

추가할 것
- 승리, 무승부 판별 함수 만들기
- 승리, 무승부시 누가 승리했는지 표시 후 초기화하기

2016년 5월 10일 화요일

Pygame (4) 폰트 불러오기, 화면에 글씨쓰기

import pygame, sys
from pygame.locals import *

pygame.init()
DISPLAYSURF = pygame.display.set_mode((400,300))
pygame.display.set_caption('Hello World!')

WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLUE = (0, 0, 128)
BLACK = (0, 0, 0)

fontObj = pygame.font.Font('font/nanum.ttf', 32)
textSurfaceObj = fontObj.render('Hello world!', True, BLACK, WHITE)
textRectObj = textSurfaceObj.get_rect()
textRectObj.center = (100, 16)

while True: # main game loop
    DISPLAYSURF.fill(WHITE)
    DISPLAYSURF.blit(textSurfaceObj, textRectObj)
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
    pygame.display.update()


---------

Anti-Aliasing == False

Anti-Aliasing == True



fontObj란 변수에 pygame.font.Font를 이용해 .ttf 형식 등의 폰트 파일을 불러와 저장합니다.
미리 폰트를 연결시킨 변수 fontObj를 이용해 render() 함수를 통해 글씨를 화면 상에 출력할 수 있습니다.
render(message, Anti-Aliasing, color, backgroundcolor)의 4개의 인자를 전달받으며,
Anti-Aliasing이 True일 경우 안티앨리어싱을 적용해 부드러운 글자를 출력하며, False를 전달할 경우 안티앨리어싱이 적용되지 않아 다소 딱딱한 글씨체를 화면 상에 출력합니다.

2016년 5월 9일 월요일

Pygame (3) 그림파일 가져오기, 객체 이동

간단한 고양이 PNG 파일을 이용하여 고양이가 움직이도록 한다.

import pygame, sys
from pygame.locals import *

pygame.init()

FPS = 30 # frames per second setting
fpsClock = pygame.time.Clock()

# set up the window
DISPLAYSURF = pygame.display.set_mode((400, 300), 0, 32)
pygame.display.set_caption('Animation')

WHITE = (255, 255, 255)
catImg = pygame.image.load('cat.png')
catx = 10
caty = 10
direction = 'right'

while True: # the main game loop
    DISPLAYSURF.fill(WHITE)

    if direction == 'right':
        catx += 5
        if catx == 280:
            direction = 'down'
    elif direction == 'down':
        caty += 5
        if caty == 220:
            direction = 'left'
    elif direction == 'left':
        catx -= 5
        if catx == 10:
             direction = 'up'
    elif direction == 'up':
        caty -= 5
        if caty == 10:
            direction = 'right'

    DISPLAYSURF.blit(catImg, (catx, caty))

    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

    pygame.display.update()
    #fpsClock.tick(FPS)

다음과 같은 코드를 실행하면 흰 화면에 고양이가 돌아다니는 모습을 볼 수 있다.



 pygame.time.Clock()을 이용해 clock(fpsClock)을 만들어준 후, fpsClock.tick(FPS)를 pygame.display.update() 뒤에 배치해 애니메이션의 속도를 조절한다.
FPS가 낮을수록 프로그램은 느리게 진행되며, 높을수록 프로그램은 빠르게 진행된다.
* (clock_name).tick()이 없는 경우, 파이썬 프로그램은 있는 힘껏 프로그램을 진행시킨다. 직접 뺀 채로 컴파일 해보는 것을 추천한다.




pygame.image.load()를 이용해 사진 파일을 불러와서 변수에 저장한 후, 다른 함수를 이용할 수 있다.
불러오려는 파일은 AppData 폴더 속 Python 폴더에 있거나, 실행 코드 파일과 같은 폴더 내에 있어야 한다. 그렇지 않은 경우 에러가 발생한다.

DISPLAYSURF.blit(catImg, (catx, caty))
blit() 함수를 이용하여 catImg를 DISPLAYSURF 위에 복사했다.
blit() 함수는 두개의 인자를 받는데, 첫 번째는 복사할 객체이고, 두 번째는 객체를 복사할 위치의 좌표이다.

Pygame (2) 픽셀, 도형 다루기

간단한 선분, 오각형, 원, 타원, 픽셀을 다뤄보고, 색을 조정해본다.

import pygame, sys
from pygame.locals import *
pygame.init()

# set up the window
DISPLAYSURF = pygame.display.set_mode((500, 400), 0, 32)
pygame.display.set_caption('Drawing')

# set up the colorsBLACK = (  0,   0,   0)
WHITE = (255, 255, 255)
RED   = (255,   0,   0)
GREEN = (  0, 255,   0)
BLUE  = (  0,   0, 255)

# draw on the surface object
DISPLAYSURF.fill(WHITE)
pygame.draw.polygon(DISPLAYSURF, GREEN, ((146, 0), (291, 106), (236, 277), (56, 277), (0, 106)))
pygame.draw.line(DISPLAYSURF, BLUE, (60, 60), (120, 60), 4)
pygame.draw.line(DISPLAYSURF, BLUE, (120, 60), (60, 120))
pygame.draw.line(DISPLAYSURF, BLUE, (60, 120), (120, 120), 4)
pygame.draw.circle(DISPLAYSURF, BLUE, (300, 50), 20, 0)
pygame.draw.ellipse(DISPLAYSURF, RED, (300, 250, 40, 80), 1)
pygame.draw.rect(DISPLAYSURF, RED, (200, 150, 100, 50))

pixObj = pygame.PixelArray(DISPLAYSURF)
pixObj[480][380] = BLACK
pixObj[482][382] = BLACK
pixObj[484][384] = BLACK
pixObj[486][386] = BLACK
pixObj[488][388] = BLACK
del pixObj

while True: # main game loop    
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
    pygame.display.update()

fill(color) : color 색깔로 화면 전체를 채운다.

pygame.draw.polygon(surface, color, pointlist, width) : 오각형을 그린다. 어떤 표면 위에 어떤 색으로, 어떤 점에서 찍을 지 인자를 전달해주면 된다. width는 옵션으로, 인자를 전달하면 오각형의 외곽선을 나타내며, 인자를 전달하지 않으면 내부가 color로 채워진다.

pygame.draw.line(surface, color, start_point, end_point, width) : 선분을 그린다. polygon 함수와 유사하며, 시작점과 끝점을 인자로 받는다.

pygame.draw.lines(surface, color, closed, pointlist, width) : 선분들을 그린다. pointlist가 선분들의 한 점이 되며, closed가 True인 경우 맨 마지막 점과 처음 점을 잇는 선분이 그려지고, False일 경우 그렇지 않다.

pygame.draw.circle(surface, color, center_point, radius, width) : 원을 그린다. 중심과 반지름을 인자로 받는다.

pygame.draw.ellipse(surface, color, bounding_rectangle, width) : 타원을 그린다. bounding_rectangle에 rectangle_tuple을 전달하면 그 직사각형에 내접하는 타원을 그린다.
* rectangle_tuple : ( x pos, y pos, width, height )

pygame.draw.rect(surface, color, rectangle_tuple, width) : 직사각형을 그린다. 

pygame.PixelArray(surface)를 이용해 surface의 픽셀을 변경시킬 수 있다. 수행하려는 작업이 끝나면 del을 이용해 지워서 unlock을 해 줘야 한다. 그렇지 않을 경우, blit()을 이용해 변경된 픽셀을 변경하려 할 때 오류가 발생한다.

Pygame (1) 시작하기

pygame을 이용한 게임 코딩에 도전하기 전에, 기본적인 틀을 알아보기 위해 게임 창을 띄우는 간단한 코드를 작성해 보았다.


import pygame
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('Hello World!')
while True: # main game loop
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
    pygame.display.update()

미리 설치했던 pygame 파일을 참조한 후,

display.set_mode()를 이용해 창의 크기를 설정한다.
display.set_caption()을 이용해 창의 제목에 삽입할 문구를 넣는다.

while문 안에서 event.type을 변경하는 별도의 문장이 없으므로
비어있는 화면이 프로그램을 종료할때까지 떠 있는다.



실행 화면.

* 참고
기존 shell에서 입출력을 위해 쓰는 함수인 print()나 input()은 CLI(Call Level Interface) 프로그램을 위한 함수이므로 Pygame상에서 입력이나 출력을 위해서는 다른 별도의 함수를 써야 한다.

Windows python 3.5 pygame 설치

http://www.lfd.uci.edu/~gohlke/pythonlibs/#pygame

http://dogdriip.tistory.com/3

첫 번째 링크에 들어가 자신에게 맞는 파이썬 버전과 32bit(or 64bit)를 확인한 후 다운을 받아줍니다.

그러면 .whl파일을 다운받을 수 있는데,

두 번째 링크를 참조하여 명령 프롬프트를 이용하여 파일을 설치할 수 있습니다.
(확장자를 빼먹을 경우 설치에 실패합니다)