[내배캠 AI코스] TIL

내일배움캠프 11일차 TIL - UP&DOWN 게임 만들기

띵제 2024. 2. 26. 20:16

오늘의 학습내용 : 1번 과제 업다운 게임 만들기

 

이 과제에서는 파이썬 프로그래밍 언어를 활용하여 업다운 게임을 만드는 것이 목표입니다. 업다운 게임은 컴퓨터가 생각한 숫자를 맞추는 게임으로, 플레이어는 숫자를 입력하고 컴퓨터가 생각한 숫자와 비교하여 "업(Up)"혹은 "다운(Down)" 힌트를 받아가며 숫자를 맞추는 게임입니다.

 

일단 생각을 정리하기 위해 말로 하나씩 풀어보자.

 

사용자가 1부터 100까지 숫자중 하나를 입력한다.

컴퓨터가 랜덤으로 숫자 하나를 픽한다.

 

두 숫자를 비교한다.

사용자 숫자가 더 크다면 DOWN을 출력하고

더 작다면 UP을 출력한다.

만약 같다면 “정답입니다”를 출력한다.

 

만약 1부터 100까지의 숫자 중 하나가 아닌

다른 숫자가 입력된다면

“1부터 100까지의 숫자를 입력해주세요”라고 뜬다.

 

시도한 횟수가 입력창 옆에 숫자로 카운트된다.

1회, 2회 이렇게.

 

파이썬 기초강의 필기한 거를 뒤지다가,

랜덤으로 숫자뽑기는 로또사이트 만들때 했던 것 같아서

챗gpt 웹개발 강의 필기한 걸 뒤지기 시작했다.

 

컴퓨터가 1부터 100까지 숫자중 하나를 출력

answer = random.sample(range(1,100))

return sorted(answer)

 

강의 들을 당시 챗gpt가 써줬던 코드가 이번 과제 힌트인

 

import random

random_number = random.randint(1, 100)

 

와 흡사하다.

 

근데 왜 챗gpt는 random.sample이라고 쓰고

과제 힌트는 random.randint이라고 나왔지?

구글링 해본다. 

 

sample 랜덤하게 여러 개의 원소를 선택한다.

randint 2개의 숫자 사이의 랜덤 정수를 리턴한다.

1부터 100사이로 범위가 정해져있어 randint 를 사용했군.

randint는 아래와 같이 사용한다고 한다.

 

import random

 

a=random.randint(1,100)

 

print(a)

 

여기서 랜덤한 숫자인 a가 특정 숫자, 예를 들어 9보다 큰지 작은지 같은지 알아보려면

if elif else 를 적어준다. 구글링을 하니 친절하게 여기까지 알려준다.

 

if a > 9 :

  print(“DOWN”)

elif a ==9 :

  print(“정답입니다.”)

else  :

  print(“UP”)

 

그런데 우리는 숫자 9가 아니라 사용자가 입력한 숫자를 저 자리에 넣어주어야 한다.

 

사용자가 입력하는 값을 받는 건.. 파이썬 기초강의에 이게 있었다고..?;;

없었잖아!!!

 

그러면.. vs코드에서 영화사이트만들기때 배웠던거를 짬뽕시켜봐야겠다.

강의노트 movie.html을 과제용 updown.html에 복붙하고 내용을 바꿨다.

 

그리고 app.py 파일에서 열심히 꼼지락 댔는데..

 

from flask import Flask, render_template, request

app = Flask(__name__)

 

@app.route('/')

def home():

    query = request.args.get('query')

    if int(query) < 9:

        print("DOWN")

    elif int(query) ==9 :

        print("정답입니다.")

    else  :

        print("UP")   

 

    return render_template('updown.html')

 

if __name__ == '__main__':  

    app.run(debug=True)

 

 

오류가 끊임없이 난다.

챗 gpt의 도움을 받아본다.

 

int() argument must be a string, a bytes-like object or a number, not 'NoneType'

이게 무슨 뜻이야?

 

챗gpt:

이 오류는 파이썬에서 ‘int()’함에수 전달된 인수가 문자열, 바이트류 객체 또는 숫자가

아니라는 것을 나타냅니다. 대부분 이 오류는 ‘int()’함수에 ’None’과 같은

유효하지 않은 값이 전달되었을 때 발생합니다.

 

예를들어, ’None’은 유효한 숫자나 문자열이 아니기 때문에 ‘int()’함수로 변환할

수 없습니다. 따라서 오류가 발생합니다.

 

이러한 오류를 방지하기 위해서는 ‘int()’함수에 전달되는 값이 항상 유효한 문자열 또는

숫자인지 확인해야 합니다. 만약에 값이 ’None’이 될 수도 있는 경우에는

이를 처리해주어야 합니다.

 

..내가 배웠던 범위 내에서 이런 내용은 없었는데;;

그냥 내가 쓴 코드를 싹 복붙하고 오류가 있는지 검토해달라고 했다.

그러니까 오류가 발생할 수 있는 몇가지 가능성이 있단다.

 

챗gpt:

1. ‘query’ 변수가 None일 경우 int()함수에 전달하면 ’NoneType’객체가 됩니다.

2. ’query’ 변수가 정수가 아닌 경우 int()함수에 전달하면 ValueError가 발생합니다.

3. ‘query’ 변수가 문자열로 변환될 수 없는 경우 int()함수에 전달하면 TypeError가 발생합니다.

 

위의 예외 상황들을 처리하지 않았기 때문에 오류가 발생할 수 있습니다.

‘int()’함수를 호출하기 전에 ‘query’ 변수가 유효한 값을 가지고 있는지 확인하는 것이 좋습니다. 이를 위해 ‘query’변수를 가져오는 부분을 ‘if’문으로 감싸고, 해당 부분을 처리하기 전에 유효성을 검사할 수 있습니다.

 

얘가 어디어디가 오류인지 알려주면 고칠 수 있을 거라 생각했는데 여전히 모르겠다.

 

아, 그럼 일단 시작 부분을 수정해야겠다.

컴퓨터가 뽑는 랜덤숫자가 들어가도록 해본다.

 

 

from flask import Flask, render_template, request, random

app = Flask(__name__)

 

@app.route('/')

def home():

    answer =random.randint(1,100)

 

    query = request.args.get('query')

    if int(query) > answer:

        print("DOWN")

    elif int(query) == answer :

        print("정답입니다.")

    elif int(query) < answer :

        print("UP")

    else  :

        print("1부터 100까지의 숫자를 입력해주세요.")   

 

    return render_template('updown.html')

 

if __name__ == '__main__':  

    app.run(debug=True)

 

 

근데 import 에 있는 random이 뭔지 얘가 못알아듣는다.

 

ImportError: cannot import name 'random' from 'flask'

 

구글링으로 문제를 해결해본다.

못찾겠다. 다들 cannot import name은 많이 검색하는데 그중 random은 별로 없는 듯 하다.

챗gpt한테 다시 가본다.

 

챗gpt:

이러한 오류는 Flask에 내장되어 있는 모듈인 ‘random’을 import하려고 시도할 때 발생합니다.

그러나 Flask에는 ‘random’모듈이 내정되어 있지 않습니다. 따라서 ‘random’모듈을 사용하려면 Python의 

내장 모듈인 ‘random’을 import 해야 합니다.

 

아 Flask에는 random이 없다고 한다.

윗 부분을

 

import random

from flask import Flask, render_template, request, random

app = Flask(__name__)

 

이렇게 바꿔준다.

 

import 오류는 해결했다.

 

브라우저를 들어가려고 하면 여전히 에러가 난다.

 

int() argument must be a string, a bytes-like object or a number, not 'NoneType'

 

이 오류가 아직 해결이 안된거다.

챗gpt가 이에 대해 설명해준 내용을 이해는 했으니,

하라는대로 try - except를 넣어줘서 none이 나올 때 오류가 나지 않도록 한다.

 

다 이식하고 아래와 같이 완성되었다.

 

import random

from flask import Flask, render_template, request

app = Flask(__name__)

 

@app.route('/')

def home():

    answer =random.randint(1,100)

    query = request.args.get('query')

 

    if query is not None :

        try :

            if int(query) > answer:

                print("DOWN")

            elif int(query) == answer :

                print("정답입니다.")

            elif int(query) < answer :

                print("UP")

            else  :

                print("1부터 100까지의 숫자를 입력해주세요.")   

        except: ValueError

        print("숫자를 입력해주세요.")

    else : 

        print("검색어를 입력하세요.")

 

    return render_template('updown.html')

 

if __name__ == '__main__':  

    app.run(debug=True)

 

그리고 나서 브라우저를 가보면 이제 에러없이 잘 나오는 걸 확인할 수 있다.

브라우저에서 숫자 5를 입력하고 터미널을 보니 ‘DOWN’이라고 뜬다. 잘 동작하고 있군.

 

그러면.. 이제 브라우저에도 UP이나 DOWN 등 표시가 나도록 해줘야겠다.

 

....조건문을 html에서 해줬어야 하는건가…?

그럼 왜 이 과제가 파이썬 과제인거야..?

 

어쨌든 브라우저에 표시되게 해보자.

 

변수를 지정해주어야 할 것 같은데 한참을 뒤적이다 챗GPT한테 물어봤다.

 

알려준대로 했는데 브라우저에 “숫자를 입력해주세요.”라고만 뜸.

 

챗GPT 한테 또 물어봄.

 

if query.isdigit():      --->입력이 숫자인지 확인해주는 함수

 

이걸 추가해줬음. 그래서 중간 부분이 아예 바뀌었다.

 

if query is not None:
        if query.isdigit():  # 입력이 숫자인지 확인합니다.
            try:
                if int(query) > answer:
                    result = "DOWN"
                elif int(query) == answer:
                    result = "정답입니다."
                elif int(query) < answer:
                    result = "UP"
            except ValueError:
                result = "1부터 100까지의 숫자를 입력해주세요."
        else:
            result = "숫자를 입력해주세요."
    else:
        result = "검색어를 입력하세요."

    return render_template('updown.html', result=result)

 

그리고 브라우저 가서 확인해보면 오케이, 잘 구현이 된다.

 

여기까지 진행하고 나서 아차 싶었다.

지금까지 만든 건 사용자가 버튼을 누를때마다 컴퓨터가 계속 새로운 랜덤숫자를 뽑아서

여러 번하면서 정답을 찾아갈 수가 없다. 답이 계속 바뀌니까..

 

그 부분을 수정해보기로 한다.

 

일단 생각을 정리해보면,

 

먼저 컴퓨터가 랜덤숫자를 뽑았다.

 

사용자는 5번째까지 시도를 해보고,

만약에 그 안에 맞았다  —> 그러면 “정답입니다”와 함께 정답을 표시해준다.

5번째까지 못 맞췄다   —> 그러면 “땡! 다음기회에.” 과 함께 정답을 표시해준다.

 

그리고 시도하는 동안 “N회입니다.” 라고 써줘야겠다.

N에 들어가는 숫자만 1,2,3..으로 카운팅되도록 해주면 되겠다.

 

구글링을 하다가 나랑 정확히 똑같은 과제를 한 듯한 블로거를 만났다.

근데 그 사람이 짠 코드는 내가 여태까지 한 거랑 차이가 좀 있었다.

일단 하나하나 필요한것만 뽑아와보았다.

 

@app.route('/')

def home():

    answer =random.randint(1,100)  #1부터 100 사이의 정수를 랜덤으로

    tries = 0  #시도횟수

    n = 5  #시도횟수 5번으로 제한

    query = request.args.get('query')

    result = ""

 

    while tries <= n: #n번까지(n과 같을때까지) 반복

        tries += 1

        if query is not None:

            if query.isdigit():  # 입력이 숫자인지 확인합니다.

                try:

                    if int(query) == answer:

                        result = "정답입니다."

                        break

                    elif int(query) > answer:

                        result = "DOWN"

                    elif int(query) < answer:

                        result = "UP"

                except ValueError:

                    result = "1부터 100까지의 숫자를 입력해주세요."

            else:

                result = "숫자를 입력해주세요."

        else:

            result = "검색어를 입력하세요."

    if tries > n:

        result = "땡! 정답은 %d입니다." % answer

 

하고 실행했더니 얘가 기회를 주지 않는다.

 

아니 5번 기회를 달라니까?!

 

다시 쪼르르 챗GPT한테 가본다.

 

문제는 반복문이 항상 5번의 기회를 주는 것이 아니라, 한 번만 실행된 후에 정답을 알려주는 것입니다. 

이것은 반복문의 조건이 잘못 설정되어 있기 때문에 발생합니다.

 

'tries' 변수가 0으로 초기화되고, 반복문이 실행될때마다 1씩 증가합니다. 그러나 'tries' 변수의 값이 'n'보다

작거나 같은지 확인하는 조건문이 반복문 안에 있기 때문에, 반복문이 한 번만 실행되고 종료되는 것입니다.

 

해결책은 반복문의 조건을 변경하여 'tries' 변수가 'n'보다 작을 때까지 반복하도록 설정하는 것입니다.또한 정답을 찾았을 때 혹은 시도 횟수가 'n'을 넘었을 때 반복문을 종료하도록 설정해야 합니다.

 

그리고 알려준 방법으로 코드를 수정해주었다.

 

@app.route('/')
def home():
    answer =random.randint(1,100)  #1부터 100 사이의 정수를 랜덤으로
    tries = 0  #시도횟수
    n = 5  #시도횟수 5번으로 제한
    query = request.args.get('query')
    result = ""

    while tries < n:  # 시도 횟수가 n보다 작을 때까지 반복
        tries += 1
        if query is not None:
            if query.isdigit():  # 입력이 숫자인지 확인합니다.
                try:
                    if int(query) == answer:
                        result = "정답입니다."
                        break
                    elif int(query) > answer:
                        result = "DOWN"
                    elif int(query) < answer:
                        result = "UP"
                except ValueError:
                    result = "1부터 100까지의 숫자를 입력해주세요."
            else:
                result = "숫자를 입력해주세요."
        else:
            result = "검색어를 입력하세요."
    if tries >= n:  # 시도 횟수가 n보다 크거나 같을 때
        result = "땡! 정답은 %d입니다." % answer

 

여전히 기회를 주지 않는다.

챗 GPT한테 가서 짜증을 냈다. 너가 알려준대로 해도 안되잖아!!!!

 

문제를 해결하기 위해서는 입력을 받는 부분과 정답을 확인하는 부분이 서로 연결되어야 합니다.

지금 코드에서는 사용자가 입력한 값이 반복문에 상관없이 단 한 번만 체크되고 있습니다.

따라서 반복문을 돌아도 사용자의 입력이 갱신되지 않고 계속 처음 입력한 값만 확인되고 있습니다.

 

해결방법은 반복문 안에서 사용자의 입력을 계속해서 받도록 해야 합니다. 그러나 이 경우에는 페이지를

새로 고쳤을 때 반복문이 처음부터 다시 시작되는 문제가 발생할 수 있습니다. 이를 해결하기 위해서는

브라우저와 서버 간에 상태를 유지하는 방법이 필요합니다.

 

가장 간단한 방법은 사용자의 세션(session)을 활용하는 것입니다. 세션은 사용자가 웹 사이트에 접속한 상태를

계속해서 유지할 수 있도록 도와주는 메커니즘입니다. Flask에서는 'session' 객체를 사용하여 세션을 다룰 수 있습니다.

 

그리고 최종적으로 아래와 같이 코드를 수정해주었다.

 

import random
from flask import Flask, render_template, request, session
app = Flask(__name__)
app.secret_key = 'super secret key'

@app.route('/')
def home():
    if 'answer' not in session:  # 세션에 정답이 없는 경우, 새로운 게임을 시작합니다.
        session['answer'] = random.randint(1, 100)  # 정답을 세션에 저장합니다.
        session['tries'] = 0  # 시도 횟수를 세션에 저장합니다.

    tries = session['tries']
    answer = session['answer']
    query = request.args.get('query')
    result = ""

    if query is not None:
        if query.isdigit():  # 입력이 숫자인지 확인합니다.
            try:
                if int(query) == answer:
                    result = "정답입니다."
                elif int(query) > answer:
                    result = "DOWN"
                elif int(query) < answer:
                    result = "UP"
                session['tries'] += 1  # 시도 횟수를 업데이트합니다.
            except ValueError:
                result = "1부터 100까지의 숫자를 입력해주세요."
        else:
            result = "숫자를 입력해주세요."
    else:
        result = "검색어를 입력하세요."

    if tries >= 5:  # 시도 횟수가 5번 이상이면, 게임을 종료합니다.
        result = "땡! 정답은 %d입니다." % answer
        session.pop('answer')  # 세션에서 정답과 시도 횟수를 삭제합니다.
        session.pop('tries')

    return render_template('updown.html', result=result)

if __name__ == '__main__':  
    app.run(debug=True)

 

..그러니까 드디어 된다!!

근데 이게 내가 배웠던 거라고..?

뭔가 많이 멀리 와버린거 같은데.. 어쨌든 1번 문제 성공(?)

 

::오늘의 회고::

TIL만 신경쓰고 사실 WIL은 신경쓰지 않으려고 했다.

근데 오늘 챗GPT랑 하루종일 1대1 과외를 받고 나니 WIL 작성이 절실해졌다.이번주 WIL은 거의 오답노트가 되지 않을까 싶다.챗GPT 해설을 해설하는 시간이 되려나..근데 이렇게 과제를 하는게 맞는건가 찜찜한 생각을 버릴 수 없다.원래는 모르는 것만 살짝씩 물어보고 최대한 내 손으로 하려고 했는데솔직히 챗GPT한테는 체면차릴 필요없이 궁금한 걸 전부 물어보게 되니까의도치않게 일대일 원데이클래스가 되어버린 것 같다.도움을 받을 때는 받더라도 얘가 알려주는 걸 다 이해하고궁극적으로 내가 직접 활용할수도 있을 정도로 공부를 추가적으로 해야겠다.

 

+ 뭔가 이상해서 튜터님께 물어보고 오니애초부터 파이썬 코드만 짰으면 됐다고...너무 멀리까지 가버렸다고 하신다..그래 어쩐지 이상하다했어. 참나 말이 안되잖아. 아놔.그냥 예습했다 쳐야겠다..