소개
선택지 위치가 바뀌어도 답을 올바르게 채점하는, 실제로 플레이할 수 있는 로컬 퀴즈를 만듭니다. Python 함수, 리스트, 딕셔너리, 조건문, 반복문을 알아야 합니다. JSON, 무작위 선택, 입력 검증은 여기서 설명합니다. 준비된 터미널 엔진이 표시와 피드백을 담당하며, 여러분은 logic.py에 세 가지 규칙을 구현합니다.
WebIDE의 Terminal 메뉴에서 New Terminal을 엽니다. 좁은 창에서는 More 안에 있습니다. 모든 명령은 이 터미널에서 실행합니다. engine.py, quiz.py, demo.py, bank.json은 그대로 두고 logic.py만 편집하세요. 읽기와 연습 예상 시간은 40분이며, 초보자 실측 시간이 아닙니다.
문제를 뽑고 선택지 객체 전체를 이동하기
이 단계에서는 순서를 아는 두 문제를 풀고, 식별 관계를 유지하면서 무작위 선택을 추가합니다. WebIDE 터미널에서 cd는 작업 디렉터리를 바꾸고 python3는 Python 파일을 실행합니다.
cd /home/labex/project
python3 demo.py
Sun에 1, 이어서 Moon에 2를 입력합니다. 둘 다 Correct!를 표시합니다. 이 고정 순서 데모는 준비되어 있습니다. 이제 만들 재사용 퀴즈에서는 선택지 위치도 바뀝니다.
cat은 텍스트 파일을 표시합니다.
cat bank.json
JSON은 리스트를 [], 딕셔너리를 {}로 표현하고 문자열은 큰따옴표로 감쌉니다. 각 문제에는 question_id, prompt, options, correct_option_id가 있습니다. 각 선택지는 option_id와 text를 짝지은 것입니다. ID는 안정적인 식별자이고, 화면 번호는 위치입니다. sky와 tide 모두 Sun/Moon을 제공하지만 Sun은 sky에서만 정답입니다. 선택지 ID는 한 문제 안에서만 유일하므로 식별에는 문제와 선택지의 쌍을 씁니다. 제공된 문제은행에서는 문제 ID와 각 문제 안의 선택지 텍스트가 유일합니다.
rng는 전달받는 난수 생성기 객체입니다. sample(items, count)는 같은 요소를 다시 뽑지 않는 새 리스트를 반환하지만, 중복 문제 ID를 고쳐 주지는 않습니다. shuffle(items)는 리스트 자체를 변경하므로 먼저 선택지를 복사하세요. 딕셔너리 전체를 옮겨 각 ID와 텍스트의 연결을 유지합니다. 요청 개수는 양수이며 min(count, len(questions))로 실제 추출 수를 문제은행 크기까지 제한합니다. 무작위성은 게임 변화를 위한 것이며 보안을 보장하지 않습니다. 동등한 추출·섞기 방법을 써도 되며 고정 시드나 순서는 요구하지 않습니다.
다음 here-document는 Python 텍스트를 씁니다. >는 logic.py를 교체하고, 따옴표로 묶은 <<'PY'는 마지막 PY까지 내용을 그대로 씁니다. 마지막 줄을 포함한 전체 블록을 실행하세요. 초기 파일이 이 두 함수로 바뀌고 입력 함수는 다음 단계에서 추가합니다.
cat > logic.py <<'PY'
def choose_questions(questions, count, rng):
return rng.sample(questions, min(count, len(questions)))
def move_options(question, rng):
options = question["options"].copy()
rng.shuffle(options)
return options
PY
이제 choose_questions는 개수가 제한된 추출 결과를 반환하고, move_options는 문제은행을 바꾸지 않은 채 재배열된 선택지 객체를 반환합니다. 터미널에 계속 입력하라는 프롬프트가 나오면 마지막 PY를 별도 줄에 입력하세요.
유효한 번호를 선택지 ID로 매핑하기
이 단계에서는 표시된 선택을 안정적인 선택지 ID로 변환합니다. 준비된 엔진이 ask(입력), tell(출력) 함수를 전달합니다. ask는 텍스트를 반환하고 strip()은 양끝 공백을 제거합니다. enumerate(..., start=1)은 각 객체와 화면 번호를 짝짓습니다. 텍스트를 str(position)과 비교하면 oops, -1, 0, 범위 밖 번호로는 답을 제출할 수 없습니다. 표시된 표기만 허용합니다. 1은 가능하지만 01은 아닙니다. q는 제출 전에 종료합니다. 유효한 답 하나를 반환하고, 그 외에는 while 반복문으로 다시 묻습니다.
같은 터미널에서 >>는 기존 두 함수를 바꾸지 않고 이 함수를 덧붙입니다.
cat >> logic.py <<'PY'
def read_answer(options, ask, tell):
while True:
text = ask("Answer (number or q): ").strip()
if text.lower() == "q":
return None
for position, option in enumerate(options, start=1):
if text == str(position):
return option["option_id"]
tell("Enter one displayed number, or q to stop.")
PY
사용 가능한 모든 문제를 플레이합니다. --count는 양수 최대 개수를 요청합니다. 100도 제공된 세 문제만 뽑습니다. --ordered가 없으면 문제와 선택지의 위치가 바뀝니다.
python3 quiz.py bank.json --count 100
첫 입력에서 oops, -1, 0, 99를 시도하세요. 매번 Enter one displayed number, or q to stop.가 나오고 같은 문제를 다시 묻습니다. 그다음 모든 문제를 텍스트로 판단해 답합니다. 별은 Sun, 조수는 Moon, 토양에서 물을 흡수하는 부분은 Root입니다. 매번 현재 화면 번호를 읽으세요. 피드백은 선택과 일치해야 하며 최종 결과는 다음과 같습니다.
Score: 3/3 answered
더 짧은 라운드를 시작하고 곧바로 q를 입력합니다.
python3 quiz.py bank.json --count 2
Score: 0/0 answered
미응답 문제는 결과에 없으며 오답이 아닙니다. 준비된 엔진이 한 번만 제출하고, 현재 문제와 선택지의 관계에 따라 피드백을 표시하고 점수를 계산합니다. 화면 번호는 저장하지 않습니다. 파일 이름을 잘못 썼을 때 Quiz error: ...와 0이 아닌 종료 코드는 게임이 성공하지 않았다는 뜻입니다. ls로 이름을 보고 위의 정확한 명령을 다시 실행하세요. Python 구문 오류라면 해당 here-document 전체를 다시 실행하고 환경을 초기화하지 마세요.
요약
유한한 무작위 추출과 안정적인 답 매핑을 갖춘, 재사용 가능한 퀴즈를 만들었습니다. 이 단원에서는 기록을 저장하지 않습니다.



