{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "# 강화학습 20줄 — 막대 세우기\n### 배우기 전 vs 배운 뒤\n\n**Connect AI LAB · 굿나잇 AI 1편 실습**\n\n정답을 알려 주지 않습니다. **잘하면 점수, 못하면 끝.** 그것만으로 AI가 스스로 배우는 걸 눈으로 봅니다.\n\n문제는 「카트 위의 막대를 쓰러뜨리지 않고 버티기」입니다. 카트를 왼쪽·오른쪽으로 밀 수 있고, 한 번 버틸 때마다 1점입니다. 최고는 500점.\n\n위에서부터 ▶ 를 누르기만 하면 됩니다. 5분이면 끝납니다."
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "---\n# 1 · 준비\n\n| 줄 | 하는 일 |\n|---|---|\n| `pip install` | 훈련장(gymnasium)과 학습 도구(stable-baselines3)를 설치합니다 |\n| `gym.make(\"CartPole-v1\")` | 「막대 세우기」 훈련장을 엽니다. 이게 **시뮬레이션**입니다 |"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "!pip -q install \"gymnasium[classic-control]\" stable-baselines3\n\nimport gymnasium as gym, numpy as np\n훈련장 = gym.make(\"CartPole-v1\")\nprint(\"훈련장 준비 완료 — 관찰:\", 훈련장.observation_space.shape[0], \"개 숫자 · 행동:\", 훈련장.action_space.n, \"가지(왼쪽/오른쪽)\")"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "---\n# 2 · 배우기 전\n\n아무렇게나 밀어 봅니다. 몇 점이나 버틸까요.\n\n| 줄 | 하는 일 |\n|---|---|\n| `점수(정책)` | 정책(어떻게 행동할지)을 받아 5판 돌리고 평균 점수를 냅니다 |\n| `action_space.sample()` | 왼쪽·오른쪽을 동전 던지듯 고릅니다 |"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "def 점수(정책, 판수=5):\n    결과 = []\n    for _ in range(판수):\n        관찰, _ = 훈련장.reset(); 합 = 0; 끝 = False\n        while not 끝:\n            행동 = 정책(관찰)\n            관찰, 보상, 종료, 시간초과, _ = 훈련장.step(행동)\n            합 += 보상; 끝 = 종료 or 시간초과\n        결과.append(합)\n    return np.mean(결과)\n\n무작위 = 점수(lambda 관찰: 훈련장.action_space.sample())\nprint(f\"배우기 전(무작위): 평균 {무작위:.0f}점 버팀  (최고 500)\")"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "---\n# 3 · 배우기\n\n정답을 안 줍니다. **버티면 점수, 넘어지면 끝**만 알려 줍니다. 2만 번 시도합니다.\n\n| 줄 | 하는 일 |\n|---|---|\n| `PPO(\"MlpPolicy\", 훈련장)` | 작은 두뇌(신경망)를 만들고 강화학습 방법(PPO)을 붙입니다 |\n| `.learn(20_000)` | 훈련장에서 2만 번 행동하며 **보상이 큰 쪽으로** 두뇌를 고칩니다 |"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "from stable_baselines3 import PPO\n\n두뇌 = PPO(\"MlpPolicy\", 훈련장, verbose=0, seed=0)\n두뇌.learn(20_000)                      # ← 2만 번 시도. 코랩에서 약 30초~1분\nprint(\"학습 끝\")"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "---\n# 4 · 배운 뒤"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "배운뒤 = 점수(lambda 관찰: int(두뇌.predict(관찰, deterministic=True)[0]))\nprint(f\"배우기 전 {무작위:.0f}점  →  배운 뒤 {배운뒤:.0f}점   (최고 500)\")"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "보통 **20점 → 500점**이 나옵니다. 정답을 한 번도 안 알려 줬는데도요.\n\n이게 강화학습입니다. 그리고 이 「훈련장」이 세일즈포스 복제본이 되면 업무 에이전트가, 로봇 시뮬레이터가 되면 피지컬 AI가 됩니다. **훈련장을 만드는 사람에게 돈이 가는 이유**입니다.\n\n---\n# 5 · 눈으로 보기 (선택)\n\n배운 두뇌가 막대를 세우는 장면을 그림 8장으로 봅니다."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "import matplotlib.pyplot as plt\n보기 = gym.make(\"CartPole-v1\", render_mode=\"rgb_array\")\n관찰, _ = 보기.reset(seed=0); 장면 = []\nfor t in range(200):\n    행동 = int(두뇌.predict(관찰, deterministic=True)[0])\n    관찰, _, 종료, 시간초과, _ = 보기.step(행동)\n    if t % 25 == 0: 장면.append(보기.render())\n    if 종료 or 시간초과: break\nfig, axes = plt.subplots(1, len(장면), figsize=(3*len(장면), 2.4))\nfor ax, 그림, i in zip(np.atleast_1d(axes), 장면, range(0, 200, 25)):\n    ax.imshow(그림); ax.set_title(f\"{i}번째\"); ax.axis(\"off\")\nplt.tight_layout(); plt.show()\nprint(f\"{t+1}번 버텼습니다\")"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "---\n### 더 해 보기\n- `learn(20_000)` 을 `2_000` 으로 줄이면? 덜 배운 두뇌가 몇 점 나오는지 보세요.\n- `CartPole-v1` 을 `LunarLander-v3` 로 바꾸면 달 착륙선입니다. (`pip install \"gymnasium[box2d]\"` 필요)\n- 훈련장 수천 개가 모여 있는 곳: https://app.primeintellect.ai/dashboard/environments\n\n*Connect AI LAB · AI CITY BUILDERS · 교재 https://www.aicitybuilders.com/gn1*"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  },
  "colab": {
   "name": "cartpole_rl.ipynb"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}