# 11. 거북이와 상호작용하기 II

1 학습 내용

1.1 목표

앞서 이벤트 프로그래밍을 배우면서 컴퓨터 상호작용이 보다 자연스러워지는 경험을 하였을 것이다. 이런 이벤트 기법을 이용해서 좀 더 발전한 상호작용을 넣어서 게임 프로그램을 만들어본다. 프로그램을 만들면서 사용자와의 상호작용이 가능하도록 만드는 이벤트 기반 프로그래밍에 대해서 심화 학습하고, 비교적 단순하지만, 전체적인 프로그램을 만드는 과정을 학습한다.

1.2 목차

[TOC]

2 주사위 굴리기 수정

우리는 앞서 사용자 키보드로부터 'q'나 'Q'를 입력받으면 종료하고, 'r' 또는 'R'에 대해서는 주사위를 다시 굴리는 프로그램을 작성하였다. 여기서는 이벤트 기능을 응용해서 'r' 또는 'R' 키 이벤트에 대해 주사위를 굴리는 프로그램으로 발전시켜 보자. 이전 주사위 굴리기 게임의 코드를 대부분 재사용하겠지만, 'q'나 'Q' 키에 대한 이벤트에 반응, 즉 프로그램을 종료시키는 것은 새로 작성해야 한다. 그것부터 살펴보자.

'q' 또는 'Q'를 눌러 거북이 프로그램 종료시키기

거북이 그래픽은 무한 대기를 하면서 사용자 입력을 처리하는 부분이 turtle.mainloop()에 있다. 따라서 여기서는 'q'나 'Q' 키 입력이벤트 처리를 통해 무한대기를 종료시켜야 한다. 강제적으로 거북이 프로그램을 종료시키려면 tutle 모듈의 bye() 함수를 호출하면 된다. 아래 코드는 캔버스 윈도우를 생성하고 'q' 또는 'Q' 키 이벤트에 대해 bye() 함수를 호출해서 종료시키고 있다.

# 11-03.py
import turtle

def exitProgram():
  turtle.bye()

win = turtle.Screen()

win.onkey(exitProgram, 'q')
win.onkey(exitProgram, 'Q')

win.listen()
turtle.mainloop()

여기서는 이미 앞에서 보았던 키보드 이벤트 처리 함수 등록 기능을 사용해서 exitProgram() 함수를 'q' 또는 'Q' 키를 눌렀을 때 호출하도록 하였다. 그리고 exitProgram() 함수에서는 단순하게 turtle 모듈의 bye() 함수를 호출하여 프로그램을 종료시킨다. 그런데 exitProgram() 함수나 turtle.bye() 함수나 인자없이 함수만 호출된다는 점에서 보면 생김새가 비슷하다. 그렇다면 exitProgram()이라는 함수를 따로 만들 것이 아니라 바로 bye() 함수를 직접 사용해도 된다.

# 11-04.py
import turtle

win = turtle.Screen()

win.onkey(turtle.bye, 'q') # turtle.bye() 를 직접 연결함
win.onkey(turtle.bye, 'Q')

win.listen()
turtle.mainloop()

주사위 굴리기 게임 구현

'r'이나 'R'키를 눌렀을 때 주사위를 굴려서 새로운 단면을 보이는 프로그램을 작성하는 과정은 앞에서 여기서 다루었던 다른 이벤트를 처리하는 프로그램들과 크게 다르지 않다. 키보드 입력을 받아서 주사위를 굴리는 것과 같은 효과를 나타내는 프로그램을 처리하는 과정은 아래와 같다.

  1. 이벤트 처리 함수 정의
  2. 캔버스 윈도우 및 거북이 로봇 생성
  3. 주사위의 단면 이미지들을 캔버스 윈도우에 등록
  4. 'r'과 'R' 키에 이벤트 처리 함수 연결
  5. 'q'와 'Q' 키에 프로그램을 종료시키는 이벤트 처리 함수 연결
  6. 윈도우의 리스너 함수와 거북이 모듈의 메인 루프 실행

여기서 이벤트 처리 함수에서는 1~6사이의 임의의 정수를 생성하고, 그 숫자 값에 따라 주사위 이미지를 선택하여 화면에 보이는 부분을 처리하도록 하는데 이미 9장에서 만들었던 함수인 rollDiceAndDrawImage() 함수를 재사용하기로 하자. 그렇다면 키보드 입력에 반응해서 주사위를 굴리는 프로그램의 코드는 아래와 같다.

# dice.py
import turtle

def rollDiceAndDrawImage():
    import random
    # 1~6사이의 임의의 정수를 생성
    n = random.randint(1, 6)
    # 6. 생성된 정수에 해당되는 주사위의 단면 이미지 선택
    t1.shape("dice" + str(n) + ".gif")
    # 7. 도장 찍기
    t1.stamp()
    t1.shape("classic")

win = turtle.Screen()
t1 = turtle.Turtle()
for i in range(1, 7):
    win.addshape("dice" + str(i) + ".gif")

win.onkey(rollDiceAndDrawImage, 'r')   
win.onkey(rollDiceAndDrawImage, 'R')   
win.onkey(turtle.bye, 'q')
win.onkey(turtle.bye, 'Q')

win.listen()
turtle.mainloop()

png

3 가위바위보 게임 수정

9장의 연습문제에서 사람과 컴퓨터가 진행하는 가위바위보 게임을 만들어보라고 했다. 그 연습문제에서는 사용자로 하여금 "가위", "바위", "보"에 해당되는 문자열을 직접 입력하도록 했었는데, 여기서는 키보드의 키를 눌러서 가위, 바위, 보를 선택하도록 한다. 또한 사용자와 컴퓨터가 어떤 것을 선택했는지를 아래처럼 그림으로 보이고, 누가 이겼는지를 화면에 출력한다. 단 여기서는 왼쪽 그림은 사람의 선택을, 오른쪽 그림은 컴퓨터의 선택을 보여주는 것으로 가정하자. 거북이를 이용해서 도장 찍듯이 이미지 파일을 보여주는 것은 이미 해보았다. 여기서는 거북이 로봇 한 개로 계속 이동시키면서 그림을 그리는 것보다는 두 개의 거북이 로봇을 만들어서 각각 사용자용 그리고 컴퓨터용으로 사용하자.

본 프로그램에서 사용할 "가위", "바위", "보" 이미지 파일들의 이름들은 각각 "scissors.gif", "rock.gif", "paper.gif"이고, 우리가 작성하는 파이썬 소스 코드와 같은 폴더에 있다고 가정한다. 이미지 파일들은 모두 320x320 해상도로 만들어졌고, 이 그림들을 적절하게 표현하기 위해서 캔버스 윈도우의 크기는 너비 700, 높이 600으로 정한다.

사용자는 아래 표에서 정리한 키를 눌러서 "가위", "바위", "보" 중 한 가지를 선택하는 것으로 하자.

선택
's' 또는 'S' 가위
'r' 또는 'R' 바위
'p' 또는 'P'

게임을 진행하는 순서는 아래와 같다

  1. 사용자가 키를 누름
  2. 사용자가 누른 키가 표에서 정리한 것과 일치하면 사용자의 거북이 로봇을 이용해서 사용자가 선택한 것을 그림으로 표현한다.
  3. 컴퓨터로 하여금 선택하도록 하고, 컴퓨터가 선택한 것을 마찬가지로 컴퓨터의 거북이 로봇을 이용해서 그림으로 표현한다.
  4. 사용자와 컴퓨터의 선택을 비교해서 누가 이겼는지를 화면에 출력한다.

게임을 진행할 수 있도록 프로그램의 진행 과정은 아래와 같다.

  1. 이벤트 처리 함수 정의
  2. 캔버스 윈도우 및 거북이 로봇 생성하고 윈도우 크기 조정
  3. 가위바위보 이미지들을 캔버스 윈도우에 등록
  4. 키에 이벤트 처리 함수 연결
  5. 윈도우의 리스너 함수와 거북이 모듈의 메인 루프 실행

키에 연결되는 이벤트 처리 함수에서는 아래처럼 게임을 진행하여야 한다.

  1. 사용자가 입력한 키에 따라 "가위", "바위", "보"에 해당되는 이미지를 그림
  2. 컴퓨터가 "가위", "바위", "보" 중에서 한 가지를 선택하도록 함
  3. 컴퓨터 거북이 로봇을 이용해서 화면에 컴퓨터가 선택한 이미지를 그림
  4. 사용자와 컴퓨터 중 누가 이겼는지 확인하고 화면에 출력

여기서 컴퓨터가 선택하는 과정은 1~3 사이의 임의의 정수를 만들고, 1은 "가위", 2는 "바위", 3은 "보"를 의미하는 것으로 한다.

처리 과정을 코드로 작성한 것은 아래와 같다. 코드를 자세히 살펴보기로 하자.

# rockscissorspaper.py
import turtle

def checkWhoWins(p, c):
    if p == 1: # 가위
        if c == 1: # 가위
            print("무승부")
        elif c == 2: # 바위
            print("컴퓨터가 이겼습니다")
        elif c == 3: # 보
            print("사람이 이겼습니다")
    elif p == 2: # 바위
        if c == 1: # 가위
            print("사람이 이겼습니다")
        elif c == 2: # 바위
            print("무승부")
        elif c == 3: # 보
            print("컴퓨터가 이겼습니다")
    elif p == 3: # 보
        if c == 1: # 가위
            print("컴퓨터가 이겼습니다")
        elif c == 2: # 바위
            print("사람이 이겼습니다")
        elif c == 3: # 보
            print("무승부")

def showPlayerImage(player):
    if player == 1: # 가위
        playerTurtle.shape("scissors.gif")
    elif player == 2: # 바위
        playerTurtle.shape("rock.gif")
    elif player == 3: # 보
        playerTurtle.shape("paper.gif")

def showComputerImage(n):
    if n == 1: # 가위
        computerTurtle.shape("scissors.gif")
    elif n == 2: # 바위
        computerTurtle.shape("rock.gif")
    elif n == 3: # 보
        computerTurtle.shape("paper.gif")

def playGame(player):
    showPlayerImage(player)
    import random
    computer = random.randint(1, 3)
    showComputerImage(computer)
    checkWhoWins(player, computer)

def playGameWithScissors():
    playGame(1)

def playGameWithRock():
    playGame(2)

def playGameWithPaper():
    playGame(3)

win = turtle.Screen()
playerTurtle = turtle.Turtle()
computerTurtle = turtle.Turtle()

win.setup(700, 600)

win.addshape("scissors.gif")
win.addshape("rock.gif")
win.addshape("paper.gif")

playerTurtle.penup()
playerTurtle.goto(-180, 0)
playerTurtle.pendown()

computerTurtle.penup()
computerTurtle.goto(180, 0)
computerTurtle.pendown()

win.onkey(playGameWithScissors, 's')
win.onkey(playGameWithScissors, 'S')
win.onkey(playGameWithRock, 'r')
win.onkey(playGameWithRock, 'R')
win.onkey(playGameWithPaper, 'p')
win.onkey(playGameWithPaper, 'P')

win.listen()
turtle.mainloop()

여기서는 여러 개의 함수들을 정의하고 있다. 먼저 checkWhoWins()함수는 사용자와 컴퓨터의 선택을 인자로 받아서 비교하고 누가 이겼는지를 화면에 출력하는 함수이다. 앞서 말한 것처럼 컴퓨터는 1~3에 해당되는 숫자 중 한 개를 무작위로 선택하게 되는데, 이 숫자 값을 인자로 전달한다. 여기서는 사용자가 선택한 것도 역시 1~3에 해당되는 숫자로 변환해서 함께 인자로 전달한다. 실제 비교하는 과정은 이미 9장에서 설명하였으므로 여기서는 더 이상 논의하지 않는다.

showPlayerImage()showComputerImage()는 각각 사용자와 컴퓨터 로봇을 이용해서 가위바위보 이미지를 보인다. playGame() 함수에서는 앞서 설명했던 게임 진행 과정을 구현하였다. 먼저 사용자의 선택에 따라 이미지를 선택해서 보여주고, 파이썬의 randint() 함수를 이용해서 컴퓨터의 선택을 만들어낸다. 그리고 해당 이미지를 보여주고 누가 이겼는지 확인한다.

playGameWithScissors(), playGameWithRock(), playGameWithPaper() 함수들은 키보드 입력에 연결되는 함수들이다. 사용자의 선택을 1~3으로 바꿔서 playGame() 함수를 호출한다. 나머지 코드들은 앞서 설명한 기본적인 작업들을 한다.

png

4 미로게임에서 함정 피하기

[문제] 이번에는 앞 절에서 만들었던 화살표 키에 실시간으로 반응하는 거북이 로봇 프로그램을 작성하였다. 이번에는 좀 더 나아가서 미로에서 거북이 로봇이 움직일 수 있는 길에 사각형 모양의 함정을 만들고, 사용자가 거북이 로봇을 움직이다가 함정에 빠지는지 확인시켜주는 프로그램을 작성한다. 만약 거북이가 함정에 빠지면, 함정이 있던 자리에 사각형 모양으로 빨간색을 칠해서 표시하며, 게임이 종료되었음을 알리는 문자열을 캔버스 윈도우 가운데 출력한다.

[문제 해결 방법] 이 문제를 해결하는 것은 아래에서 보인 것처럼 크게 몇 가지로 나누어서 생각해볼 수 있다.

  1. 함정을 사각형 모양으로 만들고, 이 함정을 미로의 어느 위치에 둘 것인지를 결정한다. 단 이때 만들어지는 함정은 프로그램 내부에서만 기록되고, 실제 사용자에게는 보이지 않는다.
  2. 사용자가 키보드를 이용해서 동서남북 방향으로 거북이 로봇을 이동시킬 수 있도록 한다.
  3. 거북이 로봇이 이동할 때마다 함정이 있는 영역에 들어가는지 확인하고, 만약 함정에 빠진다면 함정 영역을 빨간색으로 표시해주어 사용자에게 알린다. 이 작업은 아래에서 보인 것처럼 세분화 될 수 있다.
    • 거북이가 함정에 빠졌는지 확인하기 (거북이의 위치가 함정 내부에 있는지 확인)
    • 거북이가 이동할 때마다 함정에 빠지는 지 확인
    • 거북이가 함정에 빠졌다면, 함정 영역을 빨간색으로 표시해주기

위에서 나열한 3 단계의 작업 내용은 사람은 어느 정도 이해할 수 있을 지라도, 아직 컴퓨터에게 일을 시킬 수 있을 정도로 구체화되지는 않았다. 따라서 이러한 내용들을 바탕으로 좀 더 명확하게 해결 방법을 찾아보도록 하자.

함정의 위치 정하기

거북이 프로그램에서 사각형 모양의 함정을 어떻게 표현할 수 있을까? 또 미로에서 함정의 위치는 어떻게 표현할까?

  • 이미 설명했던 것처럼 거북이 프로그램이 사용하는 캔버스는 2차원 좌표계로 구성된다. 그렇다면 2차원 좌표계를 이용해서 사각형을 어떻게 표현할 수 있을까? 이전에 사각형을 그리면서 설명했던 것처럼 꼭지점 한 개와 너비, 높이로 표현할 수도 있다. 하지만 여기서는 아래 그림에서 보인 것처럼 대각선을 이루는(혹은 이웃하지 않는) 두 개 꼭지점의 좌표를 이용해서 사각형을 표현하기로 하자. 즉 그림의 P1, P4 혹은 P2, P3에 대한 정보만 있으면 사각형을 만들 수 있다.

png

  • 따라서 프로그램에서 미로의 어느 곳에 함정을 둘 지를 결정하는 것도 미로 안에서 함정을 위치시킬 곳에서 두 점의 좌표만 찾으면 되고, 프로그램에서는 이 좌표 값들을 변수에 담아서 보관한다.
  • 여기서는 편의상 그림에서 P1, P4 위치에 해당되는 좌표를 사용하기로 하고, 이들에 대한 좌표 값을 x1, y1, x2, y2 변수에 저장한다.

화살표키를 이용해서 거북이 움직이기

사용자가 키보드를 이용해서 거북이 로봇을 이동시키는 것은 실시간 미로게임에서 앞에서 프로그래밍했던 내용과 일치한다. 따라서 그 때 작성했던 코드를 재사용한다.

거북이 로봇의 위치가 함정 안에 있는 지 확인

프로그램에서의 함정은 사각형으로 표시한다고 헀다. 그리고 사각형은 두 개의 좌표 값만 있으면 표시가 가능하다고 했다. 거북이의 현재 위치가 사각형 모양의 함정 내부에 있는지 확인하려면 위에서 보인 사각형 그림처럼 거북이의 현재 좌표를 사각형을 표시하는 두 개의 좌표와 비교해보면 된다. 즉 함정을 나타내는 사각형의 좌표 값들이 각각 (x1, y1), (x2, y2)라고 하고, 거북이 로봇의 좌표를 (x, y)라고 할 때 거북이가 함정 안에 있는지는 다음에서 보인 두 가지 조건을 동시에 만족시키면 확인할 수 있다.

  minX <= x <= maxX   (minX는 x1, x2중 작은 값, maxX는 x1, x2중 큰 값)
  minY <= y <= maxY   (minY는 y1, y2중 작은 값, maxY는 y1, y2중 큰 값)

여기서 우리는 편의상 P1과 P4에 해당되는 좌표를 사용하겠다고 했었다. 그렇다면 거북이 프로그램에서 사용하는 좌표계를 고려했을 때, 자연스럽게 minX = x1, minY = y2, maxX = x2, maxY = y1이 된다. 따라서 거북이의 현재 좌표와 minX, minY, maxX, maxY를 인자로 받아 함정에 빠졌는 지 확인하는 함수를 작성하고 실제 검사해보는 코드와 실행 결과는 아래에서 보인 것과 같다.

>>> def isInTrap(x, y, minX, minY, maxX, maxY):
>>>   if x >= minX and x <= maxX and y >= minY and y <= maxY:
>>>     return True
>>>   else:
>>>     return False

>>> print(isInTrap(100, 200, -50, 100, 200, 300)) # True
>>> print(isInTrap(100, 200, 120, 100, 200, 300)) # False

True
False

코드에서 보면 minX <= x <= maxX 를 조금 다르게 표현한 것을 볼 수 있다. 많은 프로그래밍 언어들은 변수의 값이 어떤 숫자 범위 안에 있을 때 저렇게 논리 연산자를 이용해서 표현한다. 하지만 파이썬에서는 좀 더 직관적으로 수학에서 일반적으로 쓰던 표현 방식도 사용할 수 있다. 위에 있던 함수는 아래처럼 고칠 수도 있다.

>>> def isInTrap(x, y, minX, minY, maxX, maxY):
>>>   if minX <= x <= maxX and minY <= y <= maxY:
>>>     return True
>>>   else:
>>>     return False

함정 영역을 빨간색으로 표시

거북이 그래픽을 처음 설명하면서 거북이 로봇의 pencolor() 함수의 사용법에 대해 얘기했었다. 이 함수를 이용하면 거북이가 그리는 선의 색상을 바꿀 수 있었다. 이와 비슷하게 거북이에는 다각형이나 원을 그릴 때 내부를 지정된 색으로 채우는(칠하는) 기능이 있다. 즉 삼각형이나 사각형 처럼 닫혀 있는 다각형을 그릴 때 어떤 색으로 채울 지, 언제부터 채우기를 시작할지, 또 언제 색을 채우는 것을 멈출 지만 알려주면 된다. 아래 코드를 살펴보자.

# 11-01.py
import turtle
win = turtle.Screen()
t1 = turtle.Turtle()
x1 = 50
y1 = 50
x2 = 100
y2 = 100

# 1단계
t1.fillcolor("purple")    # 보라색으로 색칠함

# 2단계. 거북이에게 색을 칠할 것을 요청
t1.begin_fill()

# 3단계. 사각형을 그림
t1.penup()
t1.goto(x1, y1)
t1.pendown()
t1.goto(x2, y1)
t1.goto(x2, y2)
t1.goto(x1, y2)
t1.goto(x1, y1)

# 4단계. 거북이에게 색을 칠하는 것을 끝내도록 요청. 이 함수가 호출되면 색이 칠해짐
t1.end_fill()

png

코드를 한 줄씩 살펴보기로 하자. 1단계: 거북이 로봇으로 하여금 다각형을 그리고 내부를 채울 색상을 지정한다. 여기서는 보라색을 의미하는 "purple"를 전달했다. 빨간색으로 그리고 싶다면 "red"를 전달하면 된다. 2단계: 지금부터 그리는 선분들로 만들어지는 다각형에 대해 fillcolor()함수에 전달된 색상으로 내부를 채우도록 지시한다. 3단계: 선분을 이용해서 다각형을 그린다. 여기서는 길이가 100인 사각형을 그린다. 4단계: end_fill()함수가 호출되면 3단계에서 그렸던 다각형을 정해진 색상으로 색칠하는 과정을 끝내라고 요엋하는 것이다. 이 함수가 호출되면 그때에서야 다각형의 내부를 칠하게 된다. 이 함수 호출 뒤에 그려지는 다각형은 색칠하지 않는다.

다각형이나 원을 그리면서 내부를 칠하는데 사용되는 색상을 여기서는 fillcolor() 함수를 이용해서 지정하였다. 하지만 선의 색상을 바꿀 때 사용했던 pencolor() 함수에서도 모든 색상을 저렇게 단어로 표시할 수 없어 (R, G, B)값으로 색상을 지정할 수 있었던 것처럼, fillcolor() 함수에서도 같은 형태로 지정할 수 있다. 아래 코드는 색상을 지정하는 몇 가지 방법의 예를 보인다.

import turtle
win = turtle.Screen()
t1 = turtle.Turtle()
t1.fillcolor('red')
t1.fillcolor(0.0, 1.0, 0.0) # 초록색으로 지정. 인자를 따로 전달
t1.fillcolor((0.0, 1.0, 0.0)) # 초록색으로 지정. 인자를 한 개로 묶어서 전달
win.colormode(255) # 색상 정보를 0.0~1.0이 아니라 0~255로 지정할 수 있게 함
t1.fillcolor(0, 255, 0) # 초록색으로 지정

이제 다각형을 그리면서 특정 색상을 채우는 것을 학습했으니, 함정 영역을 표시하는 좌표 값들이 전달되면 빨간색으로 채우는 함수를 작성하면 아래 코드처럼 될 것이다.

# 11-02.py
import turtle
win = turtle.Screen()
t1 = turtle.Turtle()

def showTrapInRed(x1, y1, x2, y2):
  t1.fillcolor("red")
  t1.begin_fill()
  t1.penup()
  t1.goto(x1, y1)
  t1.pendown()
  t1.goto(x2, y1)
  t1.goto(x2, y2)
  t1.goto(x1, y2)
  t1.goto(x1, y1)
  t1.end_fill()

showTrapInRed(100, 200, 200, 300)

png

거북이가 이동할 때마다 함정에 빠지는 지 확인

거북이가 이동할 때마다 함정에 빠지는 지 확인하고 빨간색으로 표시하는 것은 앞에서 만든 함수들을 사용하면 상대적으로 간단하게 표현할 수 있다. 함정의 좌표 값들이 인자로 주어졌을 때, 거북이의 현재 위치를 파악해서 함정 안에 있는지만 확인하고, 함정에 빠졌다면 해당 함정을 빨간색으로 표시하는 코드를 함수로 작성하면 아래와 같다.

def checkTurtleInTrap(x1, y1, x2, y2):
  position = t1.pos()
  if isInTrap(position[0], position[1], x1, y1, x2, y2):
    showTrapInRed(x1, y1, x2, y2)

코드 통합

화살표키를 이용해서 사용자가 거북이 로봇을 이동시킬 때마다 미리 등록된 이벤트 처리 함수가 호출될 것이다. 그 이벤트 처리 함수에서는 거북이를 이동시키고, 새로운 위치가 함정 안에 있는지 확인한다. 그리고 함정에 빠졌다면 해당 영역을 빨간색으로 표시한다.

여기서 만들어지는 프로그램에서 함정의 위치는 (109, -239), (188, -298)로 가정한다. 그리고 앞에서는 함정의 좌표가 P1, P4에 해당되는 값이 들어온다고 가정했지만, 여기서는 함정의 위치가 주어졌을 때 minX와 maxX, minY와 maxY를 코드에서 결정한다. 이렇게 하면 혹시라도 다른 점의 좌표 값들이 주어지더라도 프로그램은 정상적으로 동작할 수 있다. 이 프로그램에서 사용할 미로 그림은 "10.maze.gif" 파일이다. 이제 문제를 해결하는 코드를 작성해보자.

# mazeWithATrap.py 
# v1과 v2 중에서 작은 값을 반환하는 함수
def getMinValue(v1, v2):
    if v1 < v2:
        return v1
    return v2

# v1과 v2 중에서 큰 값을 반환을 반환하는 함수
def getMaxValue(v1, v2):
    if v1 > v2:
        return v1
    return v2

def moveTo(x, y):
    t1.penup()
    t1.goto(x, y)
    t1.pendown()

# 거북이의 위치가 x, y이고 함정의 위치가 x1, y1, x2, y2일때 min, max를 구해서 함정에 들어갔는 지 확인
def isInTrap(x, y, x1, y1, x2, y2):
    minX = getMinValue(x1, x2)
    maxX = getMaxValue(x1, x2)
    minY = getMinValue(y1, y2)
    maxY = getMaxValue(y1, y2)
    if x >= minX and x <= maxX and y >= minY and y <= maxY:
        return True
    else:
        return False

def showTrapInRed(x1, y1, x2, y2):
    t1.fillcolor("red")
    t1.begin_fill()
    moveTo(x1, y1)
    t1.goto(x2, y1)
    t1.goto(x2, y2)
    t1.goto(x1, y2)
    t1.goto(x1, y1)
    t1.end_fill()
    moveTo(x2 + 10, y2 + 10)

def checkTurtleInTrap(x1, y1, x2, y2):
  position = t1.pos()
  if isInTrap(position[0], position[1], x1, y1, x2, y2):
    showTrapInRed(x1, y1, x2, y2)

def keyeast():
    position = t1.pos()
    t1.goto(position[0] + 10, position[1])
    checkTurtleInTrap(x1, y1, x2, y2)

def keywest():
    position = t1.pos()
    t1.goto(position[0] - 10, position[1])
    checkTurtleInTrap(x1, y1, x2, y2)

def keysouth():
    position = t1.pos()
    t1.goto(position[0], position[1] - 10)
    checkTurtleInTrap(x1, y1, x2, y2)

def keynorth():
    position = t1.pos()
    t1.goto(position[0], position[1] + 10)
    checkTurtleInTrap(x1, y1, x2, y2)

import turtle

# 함정의 위치
x1 = 109
y1 = -239
x2 = 188
y2 = -298

win = turtle.Screen()
t1 = turtle.Turtle()

win.bgpic("10.maze.gif")

# 이벤트 처리 함수 등록
win.onkey(keyeast, 'Right')   # 동
win.onkey(keywest, 'Left')    # 서
win.onkey(keysouth, 'Down')   # 남
win.onkey(keynorth, 'Up')     # 북

win.listen()
turtle.mainloop()

코드는 먼저 프로그램에서 사용될 여러 개의 함수를 정의하고 있다. getMinValue()getMaxValue()함수는 두 개의 숫자를 전달받아 둘 중 작은 값과 큰 값을 각각 반환하는 함수들이다. 함정의 위치가 주어졌을 때, x1과 x2 그리고 y1과 y2 중에서 작은 값과 큰 값을 구하기 위해 사용된다.

keyeast(), keywest(), keysouth(), keynorth() 함수들은 각각 오른쪽, 왼쪽, 아래쪽, 위쪽 화살표키에 대응하는 이벤트 처리 함수들이다. 실시간 미로 게임에서 보았던 함수와 유사하다. 다만 함정에 빠졌는 지 확인하고, 함정 영역에 들어갔다면 빨간색으로 함정을 색칠하는 기능이 추가되었다.

주 프로그램은 함정의 위치를 나타내는 두 개의 좌표를 설정하고, 캔버스와 거북이 로봇을 생성한다. 그리고 배경 영상을 미로 이미지로 설정한다. 이벤트 처리 함수들을 등록하고, 이벤트를 기다렸다가 처리 함수들을 실행시킨다.

png

[연습문제]

  1. 사용자가 1~6 사이의 숫자 키를 누르면 숫자에 해당되는 주사위 이미지를 보이는 프로그램을 작성하라.

  2. 본인만의 미로를 구성하고 함정 영역을 정한 후에, 실제 키보드로 거북이를 움직여보면서 함정 영역을 표현하는 지 확인하라. 이번에는 함정 영역을 파란색으로 표시한다.

참고로 책에서 사용된 미로는 저작권 문제로 직접 그린 것이지만, 독자들이 미로를 사용할 때에는 미로 생성 웹 서비스를 사용하는 것이 바람직할 것이다. "The Teacher's Corner"의 미로 생성 페이지(https://worksheets.theteacherscorner.net/make-your-own/maze/)를 이용하면 쉽게 미로를 생성할 수 있다.

  1. 배구공, 야구공, 테니스공, 축구공 이미지를 등록하고 'n' 키를 누를 때마다 순서대로 이미지를 바꿔서 보여주는 프로그램을 작성하라. 즉 처음 화면에서는 배구공이 보인 상태에서 시작하고 'n' 키를 누르면 야구공이 보인다. 계속해서 'n'을 누르면 다음에는 테니스공이 보이고, 그 다음에는 축구공이 보인다. 다시 'n' 키를 누르면 다시 배구공을 보여주는 것으로 돌아가도록 하고 그 뒤에 같은 방법으로 'n' 키를 누를 때마다 반복되도록 한다. 만약 사용자가 'q' 키를 누르면 종료시킨다.

  2. 's' 키를 누르면 사람과 컴퓨터가 주사위를 굴리고 나온 숫자 결과의 크기에 따라 누가 이겼는지를 표시해주는 프로그램을 작성한다. 단 사람이 굴린 주사위는 화면의 왼쪽 부분에, 컴퓨터가 굴린 주사위는 화면의 오른쪽 부분에서 확인할 수 있도록 한다.

  3. 가위바위보 게임을 수정해서 왼쪽 이미지는 사람이 선택한 것, 오른쪽 이미지는 컴퓨터가 선택한 것이라고 표시하는 프로그램을 작성한다.

  4. 거북이의 현재 위치가 원으로 되어 있는 함정 영역 내부에 있는 지 확인하는 함수를 작성하고, 이 함수를 이용해서 미로 게임의 함정을 사각형이 아니라 원으로 만들어본다.

results matching ""

    No results matching ""