앵그리매스: 함수가 배틀 게임이 되는 교실
아이들이 이기기 위해 원점과 평행이동을 먼저 물어보기 시작했다
앵그리매스: 함수가 배틀 게임이 되는 교실
DoRm이 교육 현장 문제를 어떻게 기술과 실험으로 풀어가는지 기록합니다.
라이브 데모: angrymath.vercel.app
이 글의 문제의식
교과서의 함수 그래프는 정적이고 수동적입니다. 학생 입장에서 y = x^2는 외워야 할 도형이지 쓰임을 가진 언어가 아닙니다. 저는 이걸 뒤집어보고 싶었습니다. 수학이 재미있어야 한다고 설득하는 게 아니라, 게임을 잘하려면 수학이 필요해지는 구조를 만들면 학습의 동기가 먼저 바뀌지 않을까.
앵그리매스(Angry Math)는 수학 함수식을 입력하면 그것이 포탄의 궤적이 되어 날아가는 턴제 배틀 게임입니다. Worms, 포트리스, Angry Birds를 수학 교실 쪽으로 비틀어본 시도입니다.
수학은 재미없어도 된다
오랫동안 교육 게임은 "어떻게 하면 수학을 재미있게 포장할 것인가"를 고민해왔습니다. 어려운 문제를 풀면 몬스터가 죽고, 정답을 맞히면 코인이 나옵니다. 하지만 아이들은 그 구조가 어떤 미화인지 금방 눈치챕니다. 여전히 문제를 푸는 것이고, 수학은 게임의 방해물일 뿐입니다.
반대 방향이 있습니다. 게임 자체가 먼저이고, 수학은 이기기 위한 도구가 되는 구조. 아이가 수업 중 수포자였더라도, 게임에서 이기고 싶으면 "선생님, 이차함수의 계수 a를 바꾸면 포물선이 어떻게 되나요?"를 먼저 묻게 됩니다. 그 질문이 나오는 순간이 수업의 첫 입구입니다.
앵그리매스는 이 역발상을 구현한 게임입니다.
무엇을 만들었나
게임 화면에는 두 팀의 웜이 지형 위에 배치되어 있습니다. 자기 차례가 오면 수식 입력창이 열리고, y = x^2, y = sin(x), x^2 + y^2 = 9 같은 식을 직접 타이핑해 넣습니다. Enter를 누르면 그 함수의 그래프 모양 그대로 포탄이 날아가 적 웜을 맞힙니다. 함수의 형태가 곧 조준선입니다.
맵마다 지형이 다릅니다. 직선 장애물이 놓인 맵, 포물선 형태의 협곡, 원형 방어막을 가진 요새. 각 지형은 필요한 함수를 요구합니다. 아이가 중1이면 일차함수 맵, 중3이면 이차함수 맵, 고2 기하라면 원·타원·쌍곡선 맵. 맵이 곧 교육과정 단원이 됩니다.
아이들이 수학을 먼저 물어보게 되는 3장면
장면 1 — "원점을 지나야 합니다"
학생이 첫 수식을 y = x^2 + 3이라고 입력합니다. Enter. 화면에 빨간 에러가 뜹니다. "원점을 지나야 합니다."
포탄은 웜의 발밑에서 출발합니다. 그래서 발밑을 (0, 0)으로 두는 로컬 좌표계에서 f(0) = 0이어야 합니다. y = x^2 + 3은 f(0) = 3이라 발밑이 아니라 3만큼 위에서 출발하는 궤적이 됩니다.
여기서 흥미로운 일이 벌어집니다. 학생이 에러를 보고 고개를 갸우뚱하다가 "왜 더하기 3은 안 되고 그냥 x^2는 되지?"를 묻기 시작합니다. 상수항이 세로 평행이동이라는 사실을, 교과서에서 암기한 것이 아니라 에러 메시지에서 스스로 유도하게 됩니다.
장면 2 — 평행이동 슬라이더로 명중
수식을 넣었지만 포탄이 아슬아슬하게 적 웜을 빗나갑니다. 학생은 입력창 옆의 슬라이더를 돌립니다. a를 +1, +5로 조정하자 궤적 전체가 픽셀 단위로 좌우로 움직입니다. a를 내리면 반대 방향으로 움직입니다.
이 슬라이더가 하는 일은 수식을 f(x + a)로 바꾸는 것입니다. 교실에서 가로 평행이동의 부호 방향이 반대라는 걸 외우는 데는 보통 반 학기가 걸립니다. 앵그리매스 앞에서는 "슬라이더를 오른쪽으로 돌렸는데 궤적이 왼쪽으로 간다"는 경험이 이 개념을 몸에 바로 새깁니다.
장면 3 — 이차곡선 미사일
고2 학생이 원·타원·쌍곡선 단원을 배운 뒤 이차곡선 미사일을 선택합니다. x^2 + y^2 = 9를 입력하면 반지름 3인 원이 화면에 그려지고, 그 원 궤적을 따라 포탄이 돕니다. x^2/4 - y^2/9 = 1을 넣으면 쌍곡선 두 가지가 펼쳐집니다.
이때 학생이 얻는 경험은 이것입니다. 대수식 F(x, y) = 0이 곧 기하학적 도형이라는 대응이, 입력하자마자 시각적으로 확인됩니다. 교과서의 "판별식 B^2 − 4AC로 분류한다"가 추상 규칙이 아니라 "판별식 음수면 타원처럼 닫히고, 양수면 쌍곡선처럼 두 가지가 벌어진다"가 됩니다.
설계 결정 1: 수식을 6계층으로 쪼갰다
Expression Pipeline이 이 게임의 심장입니다. 학생이 입력한 수식은 곧바로 궤적이 되지 않습니다. 디버프 적용, 발밑 원점 이동, 좌우 방향 반전 같은 게임 내 사건들이 그 사이에 개입해야 합니다.
저는 이걸 6단계로 쪼갰습니다. 1단계에서 수식 입력을 MathJSON AST로 파싱하고, 2단계에서는 "y축 대칭" 디버프가 f(x)를 f(-x)로 바꾸는 식으로 AST 레벨 치환이 일어납니다. 나머지 단계들은 로컬 좌표계 이동, 좌우 발사 방향에 맞춘 기울기 부호 맞춤을 각각 독립적으로 처리합니다.
각 계층을 독립시킨 이유는 교실 때문입니다. 나중에 교사가 "이번 수업에서는 y축 대칭 디버프만 쓰고 평행이동 디버프는 꺼주세요"라고 하면 한 계층만 on/off하면 됩니다. 이게 가능해야 수업 흐름에 맞춰 무기를 튜닝할 수 있습니다.
설계 결정 2: 반전은 화면만, 물리는 canonical
두 팀이 서로 마주 보고 앉아 있을 때, 팀 A는 왼쪽 웜이 자기편이고 팀 B는 오른쪽 웜이 자기편입니다. 양쪽 모두 내 앞에서 좌에서 우 방향으로 함수를 입력하고 싶어 합니다.
이걸 풀기 위해 저는 물리 좌표를 canonical world 하나만 유지하고, 화면 반전은 RenderTexture 거울 반사로 처리했습니다(PresentationMirror). 물리 엔진에는 좌표 반전이 전혀 전달되지 않고, 단지 화면에 나가는 픽셀만 거울처럼 뒤집힙니다.
교실에서 이게 만든 차이는 단순합니다. 학생은 좌표축이 뒤집혔는지 아닌지를 신경 쓰지 않고 함수의 형태만 생각하면 됩니다. y = x^2는 언제나 위로 볼록한 포물선이고, 팀 A든 팀 B든 같은 의미로 해석됩니다.
설계 결정 3: 자유도보다 수업이 먼저
초기 버전에서는 관통미사일, 그러니까 지형을 뚫고 직선형으로 통과하는 무기에 모든 함수를 허용했습니다. 사인, 로그, 지수, 어떤 조합이든 가능했습니다.
한 학생이 10 * sin(x^100)을 입력했습니다. 이 급진동 사인파는 맵 끝에서 끝까지 미세한 물결을 그리며 모든 지형을 뚫고 모든 웜에게 닿았습니다. 한 턴에 게임이 끝났고, 그 뒤로 수업이 진행되지 않았습니다.
그래서 관통미사일은 다항함수만 허용하도록 제약했습니다(Polynomial Constraint on Piercing Missile). sin, cos, exp, log 같은 초월함수는 입력 단계에서 거부됩니다. 학생 창의력을 일부 제한한 것은 맞습니다. 하지만 이건 창의력의 반대급부가 아니라 수업이 성립할 수 있는 조건입니다. 30분 수업 동안 여러 턴이 돌아와야 여러 학생이 여러 함수를 시도해볼 수 있고, 한 번의 오버파워 수식으로 세션이 끝나면 학습 기회 자체가 사라집니다.
시행착오: 같은 경기장에 서기
출시 직전 며칠이 가장 조마조마했습니다. 특히 D-071c라고 이름 붙인 버그가 집요했습니다. 두 학생이 같은 방에 접속했는데, 내 화면의 지형과 친구 화면의 지형이 미세하게 다르게 그려지는 것이었습니다. 웜 위치도 달랐습니다. 나는 상대 웜을 맞혔다고 생각했는데 상대 화면에서는 빗나간 것으로 처리됐습니다.
원인은 두 클라이언트가 독립된 난수 생성기로 지형과 웜을 생성하고 있었다는 것이었습니다. 온라인 대전이라면 둘이 같은 경기장을 봐야 하는데, 난수 시드가 공유되지 않으니 각자 다른 맵에서 플레이하고 있던 셈이었습니다.
해결은 mapSeed 하나를 방에 배포하고, 그 시드로 Mulberry32 PRNG를 돌려 양쪽이 동일한 지형·웜 위치를 생성하게 하는 것이었습니다(Online Multiplayer Architecture). 기술적으로는 PRNG 동기화라는 작은 패치지만 교육적으로는 더 큰 의미가 있었습니다. 교실 다자 대전이 성립하려면 모두가 같은 경기장 위에 있어야 한다. 이 한 문장이 이후 온라인 기능의 설계 원리가 됐습니다.
출시 뒤에 더 보태야 했던 것
출시하고 보니 한 가지가 빠져 있었습니다. 학생이 게임을 이긴다고 해서 자기가 어떤 함수를 쓰고 있는지 의식하는 건 아니었다는 점입니다. y = x^2 + 2x를 무심코 입력해서 적을 맞히고는, 그게 이차함수의 변형이라는 사실은 머리에 남지 않습니다.
그래서 발사 직후 웜의 머리 위에 함수 설명 말풍선을 띄우게 했습니다. 입력한 수식을 다시 한 번 분류해서 "기울기 일정한 1차 직선" "위로 볼록한 좁은 포물선" "동심원 방어막" 같은 짧은 카피(전투 톤 메시지)가 잠깐 나타납니다. 평가표 같은 톤이 아니라 게임 안의 효과음 같은 톤을 골랐습니다. 학생이 수업이 아닌 게임 흐름 안에 있을 때 그 톤이 더 잘 들리기 때문입니다.
또 하나 추가한 건 텔레포트 무기입니다. 함수가 아닌 좌표 (x, y) 한 쌍을 직접 입력해 웜을 그 자리로 이동시키는 다섯 번째 무기입니다. 함수 형태에만 학습이 쏠리던 라인업에, 좌표평면 그 자체를 다루는 경험을 끼워 넣었습니다.
다자 온라인 대전을 위해서는 NEIS 학교 코드를 매칭 키로 쓰고, 메인 화면에 학교/학년/반/개인 4단계 랭킹 패널을 띄웠습니다. 글로벌 랭킹은 학생 동기를 만들지 못하지만, "내 학교 동급생이 몇 등인가"는 다음 접속의 이유가 됩니다.
교실 반응
처음 시연하던 날 가장 인상에 남은 두 장면이 있습니다.
첫 장면. 평소 수업 시간에 한 마디도 안 하던, 표정조차 거의 변하지 않던 학생이 있었습니다. 앵그리매스를 켜자 그 학생이 게임을 시작했고, 잠시 뒤 자리에서 일어나 저에게 다가왔습니다. 웃고 있었습니다. 그리고 수학을 먼저 물었습니다. 어떤 식을 쓰면 더 멀리 가느냐고. 평가도, 점수도, 숙제도 아닌 자리에서 그 학생이 수학을 자기 입으로 꺼낸 첫 순간이었습니다. 글의 맨 앞에 적은 "왜 이걸 외워야 하나" 같은 질문이 학생의 입에서 나오는 건, 결국 자리만 만들어주면 따라오는 일이었다는 걸 그때 봤습니다.
두 번째 장면은 제가 예상하지 못한 풀이였습니다. 관통미사일은 다항함수만 허용하도록 막아두었습니다(D-056). 그래서 사인이나 코사인은 입력할 수 없습니다. 그런데 한 학생이 끝내 사인 같은 궤적으로 적을 맞혔습니다. 입력창을 들여다보니 길게 늘어선 다항식이 들어 있었습니다. 테일러 전개로 사인함수를 다항식 근사한 것이었습니다. 게임이 막아둔 자리에 학생이 더 깊은 수학으로 우회로를 만든 셈입니다. 제약이 학생 창의력을 좁히는 게 아니라 더 멀리 뻗게 한다는 D-056의 본래 의도가, 그 자리에서 살아 움직인 장면이었습니다.
앞으로
지금 앵그리매스에는 자유 입력만 있습니다. 학생이 임의로 함수를 써넣고, 그게 잘 맞으면 이기고 아니면 집니다. 다음 학기까지 붙이고 싶은 건 교사용 문제 모드입니다. 교사가 "오늘은 이차함수 평행이동만 연습하자"고 단원과 난이도를 지정하면, 학생에게 출제되는 맵과 허용되는 함수가 거기에 맞춰 좁혀지는 구조. 그리고 학생이 어떤 수식을 몇 번 시도해서 풀었는지 기록되면, 교사는 다음 수업을 그 데이터 위에서 준비할 수 있습니다.
마무리
아이들은 이기려고 원점을 외운 게 아닙니다. 이기려면 원점이 필요하다는 걸 스스로 발견했을 뿐입니다. 그 발견 위에서만 교사의 설명이 작동합니다. 앵그리매스가 교사의 설명을 대신한다고 생각하지 않습니다. 아이들이 "선생님, 왜요?"를 먼저 꺼낼 수 있는 자리를 만드는 것, 그 정도가 이 게임이 교실에서 할 수 있는 일입니다.
▶ 지금 해보기 — angrymath.vercel.app
이 프로젝트에 대해 팀과 소통하기