openfree commited on
Commit
034c20b
·
verified ·
1 Parent(s): a93f9ef

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +52 -329
app.py CHANGED
@@ -260,7 +260,6 @@ async def try_openai_api(openai_messages):
260
  # ------------------------
261
 
262
  def load_json_data():
263
-
264
  data_list = [
265
  {
266
  "name": "[게임] 테트리스 클론",
@@ -270,177 +269,10 @@ def load_json_data():
270
  "name": "[게임] 체스",
271
  "prompt": "두 명이 번갈아가며 플레이할 수 있는 체스 게임을 만들어주세요. 기본적인 체스 규칙(킹, 퀸, 룩, 비숍, 나이트, 폰의 이동 규칙)을 구현하고, 체크와 체크메이트 감지 기능이 필요합니다. 드래그 앤 드롭으로 말을 움직일 수 있게 하며, 이동 기록도 표시해주세요."
272
  },
273
- {
274
- "name": "[게임] 카드 매칭 게임",
275
- "prompt": "짝을 맞추는 메모리 카드 게임을 개발해주세요. 카드를 뒤집으면 그림이 나타나고, 같은 그림의 카드 두 장을 찾으면 점수를 얻는 방식입니다. 카드 뒤집기 애니메이션과 함께 시도 횟수를 기록하는 점수 시스템, 그리고 쉬움/보통/어려움 난이도 선택 기능(카드 수 변경)도 구현해주세요."
276
- },
277
- {
278
- "name": "[게임] 우주 슈팅 게임",
279
- "prompt": "플레이어가 우주선을 조종하여 적 우주선을 파괴하는 슈팅 게임을 만들어주세요. 키보드 방향키로 움직이고 스페이스바로 발사하며, 다양한 적 웨이브가 공격해오는 구조입니다. 충돌 감지 시스템과 함께 파워업 아이템(방패, 다중 발사, 속도 증가 등)을 구현하고, 난이도가 점진적으로 증가하는 시스템을 추가해주세요."
280
- },
281
- {
282
- "name": "[게임] 슬라이드 퍼즐",
283
- "prompt": "3x3 또는 4x4 크기의 슬라이드 퍼즐 게임을 만들어주세요. 숫자나 이미지 조각을 섞은 후, 빈 칸을 이용해 조각들을 올바른 위치로 밀어 맞추는 게임입니다. 섞기 기능과 이동 횟수 카운터, 완성 시 축하 메시지를 표시하고, 난이도 설정(크기 변경)도 구현해주세요."
284
- },
285
- {
286
- "name": "[게임] 뱀 게임 (Snake)",
287
- "prompt": "고전적인 뱀 게임을 구현해주세요. 플레이어는 방향키로 뱀을 조종하여 필드에 랜덤하게 생성되는 먹이를 먹으며, 먹이를 먹을 때마다 뱀의 길이가 늘어납니다. 자신의 몸에 부딪히거나 벽에 부딪히면 게임이 종료되며, 점수는 먹은 먹이의 수에 비례합니다. 시간이 지날수록 뱀의 이동 속도가 빨라지는 난이도 조절 기능도 추가해주세요."
288
- },
289
- {
290
- "name": "[게임] 브레이크아웃 (벽돌깨기)",
291
- "prompt": "화면 상단에 여러 줄의 벽돌이 배치된 브레이크아웃 게임을 만들어주세요. 플레이어는 화면 하단의 패들을 좌우로 움직여 공을 튕겨내어 벽돌을 깨야 합니다. 벽돌을 모두 깨면 스테이지 클리어, 공이 바닥에 떨어지면 생명이 감소합니다. 공의 속도는 시간이 지날수록 증가하며, 특수 벽돌(추가 생명, 패들 확장 등)도 구현해주세요."
292
- },
293
- {
294
- "name": "[게임] 타워 디펜스",
295
- "prompt": "길을 따라 이동하는 적들을 방어하는 타워 디펜스 게임을 개발해주세요. 플레이어는 맵의 특정 위치에 다양한 타워(기본 공격, 범위 공격, 감속 효과 등)를 설치하여 적을 물리쳐야 합니다. 웨이브 시스템으로 난이도가 점진적으로 증가하며, 적을 처치하면 자원을 얻어 타워를 업그레이드하거나 새 타워를 건설할 수 있는 경제 시스템을 구현해주세요."
296
- },
297
- {
298
- "name": "[게임] 런닝 점프 (Endless Runner)",
299
- "prompt": "캐릭터가 끝없이 달리며 장애물을 뛰어넘는 엔드리스 러너 게임을 만들어주세요. 스페이스바나 마우스 클릭으로 점프하여 다가오는 장애물(바위, 구덩이, 적 등)을 피해야 합니다. 거리에 따라 점수가 증가하며, 코인 등의 수집품을 모으는 요소와 파워업(일시적 무적, 자석 효과 등)도 추가해주세요. 시간이 지날수록 게임 속도가 빨라지는 난이도 시스템도 구현해주세요."
300
- },
301
- {
302
- "name": "[게임] 플랫포머 (Platformer)",
303
- "prompt": "2D 플랫포머 게임을 개발해주세요. 플레이어는 방향키로 캐릭터를 조종하여 발판 위를 이동하고, 스페이스바로 점프하며 코인이나 보석 같은 아이템을 수집합니다. 적 캐릭터(간단한 AI로 움직임)와 함정(가시, 떨어지는 발판 등)을 피해 목표 지점까지 도달하는 레벨 기반 구조로 만들어주세요. 체력 시스템과 체크포인트 기능도 구현해주세요."
304
- },
305
- {
306
- "name": "[게임] 미로 찾기 (Maze)",
307
- "prompt": "매번 새로운 미로를 자동 생성하는 미로 게임을 만들어주세요. 플레이어는 시작점에서 출발하여 방향키로 캐릭터를 조종해 출구를 찾아야 합니다. 미로 생성 알고리즘(예: 깊이 우선 탐색, 프림 알고리즘 등)을 활용하여 다양한 크기와 복잡도의 미로를 만들고, 타이머로 시간을 측정하며, 선택적으로 최단 경로를 보여주는 힌트 기능도 구현해주세요."
308
- },
309
- {
310
- "name": "[게임] 미니 RPG",
311
- "prompt": "간단한 턴제 RPG 게임을 개발해주세요. 플레이어는 탑다운 뷰에서 타일 기반으로 이동하며, 몬스터와 마주치면 턴제 전투가 시작됩니다. 기본 공격, 특수 스킬, 아이템 사용 등의 전투 옵션과 함께 레벨업 시스템(경험치, 능력치 상승)을 구현해주세요. 또한 전투에서 승리하면 골드와 아이템을 획득할 수 있으며, 상점에서 장비를 구매하는 기능도 추가해주세요."
312
- },
313
- {
314
- "name": "[게임] Match-3 퍼즐",
315
- "prompt": "같은 색상/모양의 아이템 3개 이상을 일렬로 맞추는 매치-3 퍼즐 게임을 만들어주세요. 아이템을 스와이프하여 위치를 바꾸고, 매치되면 아이템이 사라지며 점수를 얻는 방식입니다. 특수 매치(4개 이상, T자 모양 등)는 특수 아이템을 생성하며, 연속 매치(콤보)는 추가 점수나 보너스 효과를 제공합니다. 목표 점수 또는 제한 시간/이동 횟수 모드를 구현해주세요."
316
- },
317
- {
318
- "name": "[게임] 하늘 나는 새 (Flappy Bird)",
319
- "prompt": "플래피 버드 스타일의 게임을 개발해주세요. 플레이어는 스페이스바나 마우스 클릭으로 새를 점프시켜 위아래로 움직이는 파이프 사이를 통과해야 합니다. 파이프에 부딪히거나 화면 상단/하단에 닿으면 게임 오버이며, 통과한 파이프 쌍마다 점수가 1점씩 증가합니다. 파이프 간격은 랜덤하게 생성되며, 최고 점수를 로컬 스토리지에 저장하는 기능도 구현해주세요."
320
- },
321
- {
322
- "name": "[게임] 그림 찾기 (Spot the Difference)",
323
- "prompt": "두 개의 유사한 이미지에서 차이점을 찾는 게임을 만들어주세요. 5-10개의 차이점이 있는 이미지 쌍을 준비하고, 플레이어가 차이점을 클릭하면 표시되도록 합니다. 제한 시간 내에 모든 차이점을 찾아야 하며, 오답 클릭 시 시간 패널티가 부과됩니다. 힌트 시스템(차이점 하나를 자동으로 표시)과 난이도 선택(쉬움: 차이점이 명확, 어려움: 미묘한 차이)도 구현해주세요."
324
- },
325
- {
326
- "name": "[게임] 타이핑 게임",
327
- "prompt": "화면 상단에서 단어가 떨어지는 타이핑 게임을 개발해주세요. 플레이어는 키보드로 해당 단어를 정확히 입력하여 단어가 바닥에 닿기 전에 제거해야 합니다. 정확히 입력한 단어는 사라지고 점수를 얻으며, 난이도에 따라 단어의 길이와 떨어지는 속도가 조절됩니다. 특수 단어(빨간색 등)는 보너스 점수나 시간 추가 등의 효과를 제공하며, 일정 시간/점수마다 난이도가 상승하는 시스템도 구현해주세요."
328
- },
329
- {
330
- "name": "[게임] 미니 골프",
331
- "prompt": "물리 엔진 기반의 미니 골프 게임을 만들어주세요. 플레이어는 마우스 드래그로 공을 치는 방향과 세기를 조절하여 홀에 공을 넣어야 합니다. 다양한 장애물(모래 함정, 물웅덩이, 경사로 등)이 있는 여러 개의 코스를 구현하고, 각 홀마다 타수를 기록하여 최종 점수를 계산합니다. 바람 방향/세기 같은 환경 요소와 함께 궤적 미리보기 기능도 추가해주세요."
332
- },
333
- {
334
- "name": "[게임] 낚시 게임",
335
- "prompt": "플레이어가 낚시를 즐기는 시뮬레이션 게임을 개발해주세요. 마우스 클릭으로 낚싯줄을 던지고, 물고기가 물면 타이밍 맞추기 미니게임으로 물고기를 낚아야 합니다. 다양한 종류의 물고기(희귀도별 점수 차등)를 구현하고, 낚은 물고기에 따라 골드를 획득하여 더 좋은 낚싯대, 미끼 등을 구매할 수 있는 업그레이드 시스템을 추가해주세요. 시간대나 날씨에 따라 출현하는 물고기가 달라지는 기능도 구현해주세요."
336
- },
337
- {
338
- "name": "[게임] 빙고",
339
- "prompt": "1인용 또는 AI 대전 빙고 게임을 만들어주세요. 5x5 그리드에 1-25 숫자를 무작위로 배치하고, 번갈아가며 숫자를 선택하여 해당 칸을 마킹합니다. 가로, 세로, 대각선으로 5개의 연속된 마킹이 완성되면 빙고가 되며, 먼저 3빙고를 달성하는 쪽이 승리합니다. 컴퓨터 AI는 랜덤하게 또는 전략적으로(빙고에 가까운 라인 우선) ��자를 선택하도록 구현하고, 타이머와 승/패 기록 시스템도 추가해주세요."
340
- },
341
- {
342
- "name": "[게임] 리듬 게임",
343
- "prompt": "화면 하단에서 상단으로 노트가 올라오면 정확한 타이밍에 키를 눌러 점수를 얻는 리듬 게임을 개발해주세요. 4개의 레인(D, F, J, K 키)에 노트가 등장하며, 타이밍 정확도에 따라 Perfect, Good, Miss 등급이 표시됩니다. 배경 음악에 맞춰 노트가 생성되며, 연속 성공 시 콤보 시스템으로 추가 점수를 제공합니다. 난이도 선택(노트 속도와 밀도 조절)과 함께 최종 결과 화면(정확도, 콤보, 등급)도 구현해주세요."
344
- },
345
- {
346
- "name": "[게임] 2D 레이싱",
347
- "prompt": "탑다운 뷰의 2D 레이싱 게임을 만들어주세요. 플레이어는 방향키로 자동차를 조종하여 트랙을 따라 주행하며, 트랙 이탈 시 감속되는 메커니즘을 구현합니다. 여러 AI 경쟁자들과 경쟁하며 3바퀴를 가장 빨리 완주하는 게임 모드와 함께, 시간 제한 내에 체크포인트를 통과하는 타임 어택 모드도 구현해주세요. 다양한 차량 선택지(속도와 핸들링 특성 차등)와 부스트 아이템, 장애물 등도 추가해주세요."
348
- },
349
- {
350
- "name": "[게임] 퀴즈 게임",
351
- "prompt": "다양한 카테고리의 퀴즈를 풀어나가는 게임을 개발해주세요. 주어진 질문에 4개의 보기 중 정답을 선택하는 방식으로, 정답 시 점수를 획득하고 오답 시 생명이 감소합니다. 30초 제한 시간 내에 답을 선택해야 하며, 난이도에 따라 질문의 복잡도와 제한 시간이 조절됩니다. 50:50 힌트(오답 2개 제거), 시간 추가 등의 도움 아이템과 함께 최종 결과 요약(정답률, 카테고리별 성적)도 구현해주세요."
352
- },
353
- {
354
- "name": "[게임] 사격 갤러리 (Shooting Gallery)",
355
- "prompt": "움직이는 표적을 맞추는 사격 갤러리 게임을 만들어주세요. 마우스 클릭으로 발사하며, 다양한 속도와 패턴으로 움직이는 표적(오리, 병, 풍선 등)을 맞추면 점수를 획득합니다. 제한된 시간과 총알 수 안에 최대한 많은 점수를 얻는 것이 목표이며, 특수 표적(황금 표적 등)은 보너스 점수나 추가 시간/총알을 제공합니다. 연속 명중 시 점수 배율이 증가하는 콤보 시스템과 함께 다양한 난이도 레벨(표적 속도/수 증가)도 구현해주세요."
356
- },
357
- {
358
- "name": "[게임] 주사위 보드 게임",
359
- "prompt": "가상 주사위를 굴려 보드판을 돌아다니는 보드 게임을 개발해주세요. 플레이어는 차례대로 1-6 주사위를 굴려 말을 이동시키며, 도착한 칸에 따라 다양한 이벤트(앞으로/뒤로 이동, 한 턴 쉬기, 미니게임 등)가 발생합니다. 특수 아이템(추가 주사위, 이벤트 회피 등)을 수집하고 사용할 수 있으며, 먼저 결승점에 도달하거나 가장 많은 포인트를 모은 플레이어가 승리합니다. 1-4명의 로컬 멀티플레이어를 지원하며, AI 플레이어도 구현해주세요."
360
- },
361
- {
362
- "name": "[게임] 좀비 서바이벌",
363
- "prompt": "탑다운 뷰의 좀비 서바이벌 게임을 만들어주세요. WASD로 이동하고 마우스로 조준/발사하며, 끊임없이 몰려오는 좀비 웨이브를 최대한 오래 생존하는 것이 목표입니다. 다양한 무기(권총, 샷건, 기관총 등)와 제한된 탄약, 그리고 체력 회복 아이템과 폭탄 같은 특수 아이템을 맵에서 획득할 수 있습니다. 시간이 지날수록 좀비의 수와 속도가 증가하며, 특수 좀비(탱커, 러너 등)도 등장하는 난이도 시스템을 구현해주세요."
364
- },
365
- {
366
- "name": "[게임] 축구 페널티킥",
367
- "prompt": "축구 페널티킥 게임을 개발해주세요. 공격 시에는 방향과 파워를 조절하여 슛을 날리고, 수비 시에는 골키퍼를 좌/중앙/우 중 한 방향으로 다이빙시켜 공을 막아야 합니다. 5번의 키커-골키퍼 대결 후 더 많은 골을 넣은 쪽이 승리하며, 동점일 경우 서든데스로 승부를 가립니다. 슛의 정확도와 파워에 따라 결과가 달라지며, 골키퍼 AI는 패턴 학습을 통해 플레이어의 경향성을 파악하도록 구현해주세요. 1인 플레이와 2인 로컬 대전 모드를 모두 지원해주세요."
368
- },
369
- {
370
- "name": "[게임] 지뢰찾기 (Minesweeper)",
371
- "prompt": "클래식한 지뢰찾기 게임을 구현해주세요. NxN 크기의 그리드에 M개의 지뢰가 무작위로 배치되며, 플레이어는 좌클릭으로 칸을 열고 우클릭으로 지뢰 위치에 깃발을 표시합니다. 열린 칸에는 주변 8칸의 지뢰 수가 표시되며, 주변에 지뢰가 없는 칸을 열면 연쇄적으로 주변 칸들이 열립니다. 지뢰가 있는 칸을 열면 게임 오버, 지뢰가 아닌 모든 칸을 열면 승리입니다. 난이도 설정(쉬움: 9x9/10개, 중간: 16x16/40개, 어려움: 30x16/99개)과 함께 첫 클릭은 항상 안전하도록 구현해주세요."
372
- },
373
- {
374
- "name": "[게임] Connect Four",
375
- "prompt": "두 플레이어가 번갈아가며 7x6 그리드에 색깔 디스크를 떨어뜨려 가로, 세로, 대각선으로 4개의 연속된 디스크를 만드는 Connect Four 게임을 개발해주세요. 플레이어는 열을 클릭하여 디스크를 해당 열의 가장 아래 빈 칸에 배치합니다. 4개의 연속된 디스크를 먼저 만드는 플레이어가 승리하며, 모든 칸이 차면 무승부입니다. 1인 플레이(AI 대전)과 2인 로컬 대전 모드를 구현하고, AI는 최소한 1단계 앞을 내다보는 논리로 작동하도록 해주세요."
376
- },
377
- {
378
- "name": "[게임] 스크래블 (단어 퍼즐)",
379
- "prompt": "글자 타일을 배치하여 단어를 만드는 스크래블 스타일의 단어 게임을 만들어주세요. 각 플레이어는 7개의 글자 타일을 받고, 이를 보드에 배치하여 가로나 세로로 단어를 형성합니다. 새 단어는 기존 단어와 반드시 연결되어야 하며, 각 타일에는 점수가 있어 단어의 총점이 계산됩니다. 특수 칸(2배 글자 점수, 3배 단어 점수 등)을 활용한 전략적 배치가 가능하며, 사전 검증 기능으로 유효한 단어만 허용합니다. 1-4인 로컬 멀티플레이어와 AI 대전을 지원해주세요."
380
- },
381
- {
382
- "name": "[게임] 탱크 배틀",
383
- "prompt": "2D 환경에서 진행되는 탱크 전투 게임을 개발해주세요. 플레이어는 WASD로 탱크를 조종하고, 마우스로 포탑을 조준하여 클릭으로 발사합니다. 파괴 가능한 지형(벽돌, 나무 등)과 파괴 불가능한 장애물(강철, 물 등)이 있는 맵에서 적 탱크들과 전투를 벌입니다. 다양한 무기(기본 포탄, 확산탄, 레이저 등)와 아이템(속도 증가, 방어력 강화, 추가 생명 등)을 구현하고, 스테이지별로 증가하는 적 AI 난이도와 보스 전투도 추가해주세요."
384
- },
385
- {
386
- "name": "[게임] 젬 크러쉬",
387
- "prompt": "3개 이상의 같은 보석을 맞추어 제거하는 퍼즐 게임을 만들어주세요. 인접한 두 보석을 스왑하여 매치를 만들며, 매치된 보석이 사라지면 위의 보석들이 떨어지고 새 보석이 채워집니다. 4개 이상 매치 시 특수 보석(가로/세로 폭발, 주변 9칸 폭발 등)이 생성되며, 연쇄 매치가 발생하면 콤보 점수가 추가됩니다. 제한 시간 또는 제한 이동 횟수 내에 목표 점수를 달성하는 레벨 기반 진행 구조와 함께, 특수 미션(특정 색상 N개 제거, 장애물 파괴 등)도 구현해주세요."
388
- },
389
- {
390
- "name": "[게임] 방어 타워",
391
- "prompt": "단일 타워가 끊임없이 몰려오는 적들을 격퇴하는 타워 디펜스 게임을 개발해주세요. 화면 중앙의 타워는 자동으로 가장 가까운 적을 향해 발사하며, 플레이어는 웨이브 사이에 획득한 자원으로 타워를 업그레이드(공격력, 공격 속도, 범위 등)할 수 있습니다. 시간이 지날수록 더 강력하고 다양한 적(빠른 적, 방어력 높은 적, 분열하는 적 등)이 등장하며, 타워의 체력이 0이 되면 게임 오버입니다. 특수 능력(범위 공격, 일시 정지, 즉시 회복 등)과 함께 생존한 웨이브 수에 따른 랭킹 시스템도 구현해주세요."
392
- },
393
- {
394
- "name": "[게임] 좀비 러너",
395
- "prompt": "캐릭터가 끝없이 달리며 좀비와 장애물을 피하는 사이드 스크롤링 러너 게임을 만들어주세요. 스페이스바로 점프, S키로 슬라이딩하여 다양한 장애물(웅덩이, 장벽, 좀비 무리 등)을 피해야 합니다. 코인과 파워업(일시적 무적, 자석 효과, 속도 감소 등)을 수집하며, 특정 구간마다 미니 보스 좀비와의 간단한 전투도 포함됩니다. 거리에 따라 점수가 증가하고, 코인으로 캐릭터 업그레이드(더블 점프, 체력 증가 등)를 구매할 수 있는 시스템도 구현해주세요."
396
- },
397
- {
398
- "name": "[게임] 액션 RPG",
399
- "prompt": "탑다운 뷰의 간단한 액션 RPG 게임을 개발해주세요. WASD로 이동하고, 마우스 클릭으로 기본 공격, 1-4 키로 특수 스킬을 사용합니다. 플레이어는 몬스터를 처치하며 경험치와 아이템을 획득하고, 레벨업 시 능력치(공격력, 체력, 속도 등)를 향상시킵니다. 다양한 무기와 방어구를 착용할 수 있으며, 스킬 트리 시스템으로 캐릭터를 특화��킬 수 있습니다. 여러 지역과 보스 몬스터, 간단한 퀘스트 시스템도 구현해주세요."
400
- },
401
- {
402
- "name": "[게임] 마인크래프트 미니 클론",
403
- "prompt": "간단한 마인크래프트 스타일의 블록 기반 샌드박스 게임을 만들어주세요. WASD로 이동, 스페이스바로 점프하며, 마우스 좌클릭으로 블록 파괴, 우클릭으로 블록 설치가 가능합니다. 다양한 종류의 블록(흙, 돌, 나무 등)을 구현하고, 인벤토리 시스템으로 수집한 블록을 관리합니다. 간단한 제작 시스템(나무→나무 판자, 돌→석재 등)과 낮/밤 주기, 기초적인 물리 엔진(모래/물 흐름)도 구현해주세요."
404
- },
405
- {
406
- "name": "[게임] 디펜스 게임 (Plants vs Zombies 스타일)",
407
- "prompt": "식물 대 좀비 스타일의 레인 디펜스 게임을 개발해주세요. 맵은 5-6개의 가로 레인으로 구성되며, 플레이어는 자원(태양 에너지)을 모아 다양한 방어 유닛(직선 공격, 범위 공격, 감속 효과 등)을 배치합니다. 레인을 따라 다양한 유형의 적(기본 적, 방어력 높은 적, 빠른 적 등)이 접근하며, 이들이 맵 끝에 도달하면 생명이 감소합니다. 스테이지별로 적의 유형과 패턴이 다양해지며, 새로운 방어 유닛이 잠금 해제되는 진행 시스템을 구현해주세요."
408
- },
409
- {
410
- "name": "[게임] 포켓몬 배틀 시뮬레이터",
411
- "prompt": "턴제 방식의 포켓몬 배틀 시뮬레이터를 만들어주세요. 플레이어는 6마리의 포켓몬 팀을 구성하여 AI 트레이너와 대전합니다. 각 포켓몬은 타입(불, 물, 풀 등), 능력치(공격, 방어, 속도 등), 4개의 기술을 가지며, 턴마다 공격/교체/아이템 사용 중 하나를 선택합니다. 타입 상성 시스템(불>풀, 물>불 등)과 상태 이상(독, 마비, 화상 등)을 구현하고, 대전 후 경험치를 획득하여 레벨업하는 시스템도 추가해주세요."
412
- },
413
- {
414
- "name": "[게임] 카드 배틀 (Hearthstone 스타일)",
415
- "prompt": "리소스 관리형 카드 배틀 게임을 개발해주세요. 플레이어와 AI는 턴제로 진행하며, 매 턴마다 1씩 증가하는 마나를 사용하여 카드를 플레이합니다. 카드는 크게 유닛(공격력/체력 수치 보유), 주문(즉시 효과), 장비(유닛 강화) 등으로 구성되며, 상대 플레이어의 체력을 0으로 만들면 승리합니다. 30-40장의 카드로 덱을 구성하고, 다양한 카드 시너지와 특수 효과(도발, 돌진, 보호막 등)를 구현해주세요."
416
- },
417
- {
418
- "name": "[게임] 음악 작곡 시뮬레이터",
419
- "prompt": "사용자가 간단한 음악을 작곡하고 재생할 수 있는 작곡 게임을 만들어주세요. 격자 형태의 인터페이스에서 가로축은 시간, 세로축은 음높이를 나타내며, 격자를 클릭하여 노트를 배치합니다. 다양한 악기 소리(피아노, 기타, 드럼 등)를 선택할 수 있고, 템포와 볼륨 조절도 가능합니다. 작곡한 음악을 저장하고 불러오는 기능과 함께, 간단한 튜토리얼 모드와 리듬 게임 모드(작곡한 음악에 맞춰 플레이)도 구현해주세요."
420
- },
421
- {
422
- "name": "[게임] 도시 건설 시뮬레이션",
423
- "prompt": "간단한 도시 건설/관리 시뮬레이션 게임을 개발해주세요. 플레이어는 그리드 기반 맵에 주거, 상업, 공업 구역과 도로, 공원, 발전소 등의 시설을 배치하여 도시를 발전시킵니다. 구역별 균형, 교통 연결성, 오염 관리 등을 고려해야 하며, 인구와 세금 수입에 따라 예산을 관리합니다. 시간이 지남에 따라 도시가 성장하고 새로운 건물이 해제되며, 자연재해나 경제 위기 같은 이벤트도 랜덤하게 발생하는 시스템을 구현해주세요."
424
- },
425
- {
426
- "name": "[게임] 드로잉 인식 게임",
427
- "prompt": "플레이어가 주어진 제시어를 그림으로 그리면 AI가 이를 인식하는 그림 맞추기 게임을 만들어주세요. 캔버스에 마우스로 그림을 그리고, 간단한 이미지 인식 알고리즘(기본 형태/색상 분석)을 통해 AI가 그림을 유추합니다. 제한 시간 내에 AI가 맞출 수 있도록 그림을 그려야 하며, 난이도별로 다양한 카테고리(동물, 음식, 교통수단 등)의 제시어가 주어집니다. 점수 시스템과 함께 멀티플레이어 모드(한 명이 그리고 다른 플레이어가 맞추는 방식)도 구현해주세요."
428
- },
429
- {
430
- "name": "[게임] 로그라이크 던전 크롤러",
431
- "prompt": "절차적으로 생성되는 던전을 탐험하는 로그라이크 게임을 개발해주세요. 플레이어는 방향키로 이동하며, 랜덤 생성된 던��에서 몬스터와 전투하고 아이템을 수집합니다. 턴제 기반 전투 시스템과 함께 다양한 무기, 방어구, 포션 등의 아이템을 구현하고, 죽으면 처음부터 다시 시작하되 영구적 업그레이드를 구매할 수 있는 시스템도 추가해주세요. 다양한 적 유형, 함정, 보스 전투와 함께 여러 캐릭터 클래스(전사, 마법사, 도적 등)도 구현해주세요."
432
- },
433
  ]
434
  return data_list
435
 
436
- def load_all_templates():
437
- """
438
- 모든 템플릿을 하나로 보여주는 함수
439
- """
440
- return create_template_html("🎮 모든 게임 템플릿", load_json_data())
441
-
442
-
443
-
444
  def create_template_html(title, items):
445
  """
446
  이미지 없이 템플릿 HTML 생성
@@ -488,11 +320,12 @@ def create_template_html(title, items):
488
  </style>
489
  <div class="prompt-grid">
490
  """
 
491
  for item in items:
492
  card_html = f"""
493
- <div class="prompt-card" onclick="copyToInput(this)" data-prompt="{html.escape(item.get('prompt', ''))}">
494
- <div class="card-name">{html.escape(item.get('name', ''))}</div>
495
- <div class="card-prompt">{html.escape(item.get('prompt', ''))}</div>
496
  </div>
497
  """
498
  html_content += card_html
@@ -524,35 +357,23 @@ def load_all_templates():
524
  # 5) 배포/부스트/기타 유틸
525
  # ------------------------
526
 
527
- def generate_space_name():
528
- letters = string.ascii_lowercase
529
- return ''.join(random.choice(letters) for i in range(6))
530
-
531
-
532
-
533
-
534
  def remove_code_block(text):
535
  """
536
  More robust function to extract code from markdown code blocks
537
  텍스트에서 ```html 및 ``` 태그를 완전히 제거하는 함수
538
  """
539
- # ```html 태그로 둘러싸인 코드 블록 찾기
540
  pattern = r'```html\s*([\s\S]+?)\s*```'
541
  match = re.search(pattern, text, re.DOTALL)
542
  if match:
543
  return match.group(1).strip()
544
 
545
- # 일반 코드 블록 처리
546
  pattern = r'```(?:\w+)?\s*([\s\S]+?)\s*```'
547
  match = re.search(pattern, text, re.DOTALL)
548
  if match:
549
  return match.group(1).strip()
550
 
551
- # 텍스트에 ```html과 ```가 포함된 경우 명시적으로 제거
552
  text = re.sub(r'```html\s*', '', text)
553
  text = re.sub(r'\s*```', '', text)
554
-
555
- # 코드 블록이 없는 경우 원본 텍스트 반환
556
  return text.strip()
557
 
558
  def optimize_code(code: str) -> str:
@@ -563,63 +384,47 @@ def optimize_code(code: str) -> str:
563
  if not code or len(code.strip()) == 0:
564
  return code
565
 
566
- # 코드 라인수 체크
567
  lines = code.split('\n')
568
- if len(lines) <= 200: # 이미 충분히 짧으면 그대로 반환
569
  return code
570
 
571
- # 1. 불필요한 주석 제거 (/* */, //, <!-- --> 등)
572
  comment_patterns = [
573
- r'/\*[\s\S]*?\*/', # /* 여러 줄 주석 */
574
- r'//.*?$', # // 한 줄 주석
575
- r'<!--[\s\S]*?-->' # <!-- HTML 주석 -->
576
  ]
577
-
578
  cleaned_code = code
579
  for pattern in comment_patterns:
580
  cleaned_code = re.sub(pattern, '', cleaned_code, flags=re.MULTILINE)
581
 
582
- # 2. 불필요한 공백 라인 제거
583
  cleaned_lines = []
584
  empty_line_count = 0
585
  for line in cleaned_code.split('\n'):
586
  if line.strip() == '':
587
  empty_line_count += 1
588
- if empty_line_count <= 1: # 연속 공백 라인은 하나만 유지
589
  cleaned_lines.append('')
590
  else:
591
  empty_line_count = 0
592
  cleaned_lines.append(line)
593
 
594
- # 3. 불필요한 들여쓰기 최적화
595
  cleaned_code = '\n'.join(cleaned_lines)
596
-
597
- # 4. console.log 제거 (디버깅용 코드)
598
  cleaned_code = re.sub(r'console\.log\(.*?\);', '', cleaned_code, flags=re.MULTILINE)
599
-
600
- # 5. 과도한 공백 제거 (CSS에서는 조심)
601
  cleaned_code = re.sub(r' {2,}', ' ', cleaned_code)
602
-
603
  return cleaned_code
604
 
605
  def send_to_sandbox(code):
606
  """
607
- Improved function to create iframe with proper code cleaning and optimization
608
- ```html 태그가 확실히 제거된 코드를 최적화하여 iframe으로 렌더링
609
  """
610
- # 코드에서 마크다운 표기 제거
611
  clean_code = remove_code_block(code)
612
-
613
- # 코드 최적화
614
  clean_code = optimize_code(clean_code)
615
 
616
- # ```html 태그가 여전히 있으면 명시적으로 제거
617
  if clean_code.startswith('```html'):
618
  clean_code = clean_code[7:].strip()
619
  if clean_code.endswith('```'):
620
  clean_code = clean_code[:-3].strip()
621
 
622
- # 기본 HTML 구조 추가
623
  if not clean_code.strip().startswith('<!DOCTYPE') and not clean_code.strip().startswith('<html'):
624
  clean_code = f"""<!DOCTYPE html>
625
  <html>
@@ -632,16 +437,11 @@ def send_to_sandbox(code):
632
  {clean_code}
633
  </body>
634
  </html>"""
635
-
636
- # iframe 생성
637
  encoded_html = base64.b64encode(clean_code.encode('utf-8')).decode('utf-8')
638
  data_uri = f"data:text/html;charset=utf-8;base64,{encoded_html}"
639
  return f'<iframe src="{data_uri}" width="100%" height="920px" style="border:none;"></iframe>'
640
 
641
  def boost_prompt(prompt: str) -> str:
642
- """
643
- '증강' 버튼 눌렀을 때 프롬프트를 좀 더 풍부하게 생성하되 간결함 유지
644
- """
645
  if not prompt:
646
  return ""
647
  boost_system_prompt = """당신은 웹 게임 개발 프롬프트 전문가입니다.
@@ -661,12 +461,12 @@ def boost_prompt(prompt: str) -> str:
661
  - 최소한의 필수 게임 요소만 포함
662
  """
663
  try:
664
- # Claude API 시도
665
  try:
666
  response = claude_client.messages.create(
667
  model="claude-3-7-sonnet-20250219",
668
- max_tokens=10000, # 출력 길이 제한
669
- temperature=0.3, # 온도 낮게
670
  messages=[{
671
  "role": "user",
672
  "content": f"다음 게임 프롬프트를 분석하고 증강하되, 간결함을 유지하세요: {prompt}"
@@ -677,7 +477,6 @@ def boost_prompt(prompt: str) -> str:
677
  return response.content[0].text
678
  raise Exception("Claude API 응답 형식 오류")
679
  except Exception:
680
- # OpenAI API로 fallback
681
  completion = openai_client.chat.completions.create(
682
  model="gpt-4",
683
  messages=[
@@ -691,10 +490,7 @@ def boost_prompt(prompt: str) -> str:
691
  return completion.choices[0].message.content
692
  raise Exception("OpenAI API 응답 형식 오류")
693
  except Exception:
694
- # 실패 시 원본 그대로 반환
695
  return prompt
696
-
697
-
698
 
699
  def handle_boost(prompt: str):
700
  try:
@@ -704,29 +500,17 @@ def handle_boost(prompt: str):
704
  return prompt, gr.update(active_key="empty")
705
 
706
  def history_render(history: History):
707
- """
708
- 히스토리 Drawer 열고, Chatbot UI에 히스토리 반영
709
- """
710
  return gr.update(open=True), history
711
 
712
  def execute_code(query: str):
713
- """
714
- Improved function to execute code directly from input
715
- 코드 실행 시 ```html 태그를 확실히 제거
716
- """
717
  if not query or query.strip() == '':
718
  return None, gr.update(active_key="empty")
719
  try:
720
- # 코드 정제 - 마크다운 태그 철저히 제거
721
  clean_code = remove_code_block(query)
722
-
723
- # ```html 태그가 여전히 있으면 명시적으로 제거
724
  if clean_code.startswith('```html'):
725
  clean_code = clean_code[7:].strip()
726
  if clean_code.endswith('```'):
727
  clean_code = clean_code[:-3].strip()
728
-
729
- # HTML 구조 추가
730
  if not clean_code.strip().startswith('<!DOCTYPE') and not clean_code.strip().startswith('<html'):
731
  if not ('<body' in clean_code and '</body>' in clean_code):
732
  clean_code = f"""<!DOCTYPE html>
@@ -740,7 +524,6 @@ def execute_code(query: str):
740
  {clean_code}
741
  </body>
742
  </html>"""
743
-
744
  return send_to_sandbox(clean_code), gr.update(active_key="render")
745
  except Exception as e:
746
  print(f"Execute code error: {str(e)}")
@@ -762,7 +545,6 @@ class Demo:
762
  if _history is None:
763
  _history = []
764
 
765
- # 프롬프트 제한 및 강화
766
  query = f"""
767
  다음 게임을 제작해주세요.
768
  중요 요구사항:
@@ -778,7 +560,7 @@ class Demo:
778
  system_message = messages[0]['content']
779
 
780
  claude_messages = [
781
- {"role": msg["role"] if msg["role"] != "system" else "user", "content": msg["content"]}
782
  for msg in messages[1:] + [{'role': Role.USER, 'content': query}]
783
  if msg["content"].strip() != ''
784
  ]
@@ -792,7 +574,6 @@ class Demo:
792
  openai_messages.append({"role": "user", "content": query})
793
 
794
  try:
795
- # "Generating code..." 출력
796
  yield [
797
  "Generating code...",
798
  _history,
@@ -803,7 +584,6 @@ class Demo:
803
  await asyncio.sleep(0)
804
  collected_content = None
805
  try:
806
- # Claude API 시도
807
  async for content in try_claude_api(system_message, claude_messages):
808
  yield [
809
  content,
@@ -815,7 +595,6 @@ class Demo:
815
  await asyncio.sleep(0)
816
  collected_content = content
817
  except Exception:
818
- # OpenAI fallback
819
  async for content in try_openai_api(openai_messages):
820
  yield [
821
  content,
@@ -826,12 +605,11 @@ class Demo:
826
  ]
827
  await asyncio.sleep(0)
828
  collected_content = content
 
829
  if collected_content:
830
- # 코드 크기 확인하고 필요하면 경고 추가
831
  clean_code = remove_code_block(collected_content)
832
  code_lines = clean_code.count('\n') + 1
833
-
834
- if code_lines > 700: # 여유 있게 700줄로 체크
835
  warning_msg = f"""
836
  ⚠️ **경고: 생성된 코드가 너무 깁니다 ({code_lines}줄)**
837
  이로 인해 실행 시 오류가 발생할 수 있습니다. 다음과 같이 시도해 보세요:
@@ -841,9 +619,7 @@ class Demo:
841
  ```html
842
  {clean_code[:2000]}
843
  ... (코드가 너무 깁니다) ... """
844
-
845
  collected_content = warning_msg
846
- # 히스토리에는 추가하지 않음
847
  yield [
848
  collected_content,
849
  _history,
@@ -852,14 +628,12 @@ class Demo:
852
  gr.update(open=True)
853
  ]
854
  else:
855
- # 히스토리 갱신
856
  _history = messages_to_history([
857
  {'role': Role.SYSTEM, 'content': system_message}
858
  ] + claude_messages + [{
859
  'role': Role.ASSISTANT,
860
  'content': collected_content
861
  }])
862
- # 최종 결과(코드) + 샌드박스 미리보기
863
  yield [
864
  collected_content,
865
  _history,
@@ -891,7 +665,7 @@ theme = gr.themes.Soft(
891
  )
892
 
893
  with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
894
- # 헤더 HTML을 동적으로 업데이트 가능하도록 그라디오 HTML 컴포넌트로 변경
895
  header_html = gr.HTML("""
896
  <div class="app-header">
897
  <h1>🎮 Vibe Game Craft</h1>
@@ -1047,12 +821,9 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1047
  const url = document.getElementById('deploy-banner-url').href;
1048
  navigator.clipboard.writeText(url)
1049
  .then(() => {
1050
- // 복사 성공 시 버튼 텍스트 변경
1051
  const copyBtn = document.querySelector('.deploy-banner-copy-btn');
1052
  const originalText = copyBtn.textContent;
1053
  copyBtn.textContent = '복사됨!';
1054
-
1055
- // 1초 후 원래 텍스트로 복원
1056
  setTimeout(() => {
1057
  copyBtn.textContent = originalText;
1058
  }, 1000);
@@ -1067,14 +838,10 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1067
  const bannerUrlContainer = document.getElementById('deploy-banner-url-container');
1068
  const bannerUrl = document.getElementById('deploy-banner-url');
1069
 
1070
- // 배너 클래스 설정
1071
  banner.className = 'deploy-banner ' + type;
1072
-
1073
- // 타이틀과 메시지 설정
1074
  bannerTitle.textContent = title;
1075
  bannerMessage.textContent = message;
1076
 
1077
- // URL이 있으면 표시
1078
  if (url) {
1079
  bannerUrl.href = url;
1080
  bannerUrl.textContent = url;
@@ -1083,7 +850,6 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1083
  bannerUrlContainer.style.display = 'none';
1084
  }
1085
 
1086
- // 배너 표시
1087
  banner.style.display = 'block';
1088
  }
1089
  </script>
@@ -1092,7 +858,7 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1092
  history = gr.State([])
1093
  setting = gr.State({"system": SystemPrompt})
1094
 
1095
- # 배포 상태를 저장할 상태 변수
1096
  deploy_status = gr.State({
1097
  "is_deployed": False,
1098
  "status": "",
@@ -1113,7 +879,7 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1113
  show_label=False, flushing=False, height=960, elem_classes="history_chatbot"
1114
  )
1115
 
1116
- # templates Drawer (하나로 통합)
1117
  with antd.Drawer(
1118
  open=False,
1119
  title="게임 템플릿",
@@ -1126,10 +892,10 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1126
  session_history = gr.HTML(elem_classes="session-history")
1127
  close_btn = antd.Button("닫기", type="default", elem_classes="close-btn")
1128
 
1129
- # 좌우 레이아웃 + 상단 정렬
1130
  with antd.Row(gutter=[32, 12], align="top", elem_classes="equal-height-container") as layout:
1131
 
1132
- # 왼쪽 Col: 미리보기 (상단 정렬)
1133
  with antd.Col(span=24, md=16, elem_classes="equal-height-col"):
1134
  with ms.Div(elem_classes="right_panel panel"):
1135
  gr.HTML(r"""
@@ -1149,17 +915,16 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1149
  with antd.Tabs.Item(key="render"):
1150
  sandbox = gr.HTML(elem_classes="html_content")
1151
 
1152
- # 오른쪽 Col: 상단에 메뉴 버튼들과 액션 버튼들, 그 아래 입력창 + 배포 결과
1153
  with antd.Col(span=24, md=8, elem_classes="equal-height-col"):
1154
- # 우측 상단 메뉴 + 액션 버튼
1155
  with antd.Flex(vertical=True, gap="small", elem_classes="right-top-buttons"):
1156
- # ── (1) 상단 메뉴 Bar (코드보기, 히스토리, 템플릿) ──
1157
  with antd.Flex(gap="small", elem_classes="setting-buttons", justify="space-between"):
1158
  codeBtn = antd.Button("🧑‍💻 코드 보기", type="default", elem_classes="code-btn")
1159
  historyBtn = antd.Button("📜 히스토리", type="default", elem_classes="history-btn")
1160
  template_btn = antd.Button("🎮 템플릿", type="default", elem_classes="template-btn")
1161
 
1162
- # ── (2) 액션 버튼들 ──
1163
  with antd.Flex(gap="small", justify="space-between", elem_classes="action-buttons"):
1164
  btn = antd.Button("전송", type="primary", size="large", elem_classes="send-btn")
1165
  boost_btn = antd.Button("증강", type="default", size="large", elem_classes="boost-btn")
@@ -1167,9 +932,7 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1167
  deploy_btn = antd.Button("배포", type="default", size="large", elem_classes="deploy-btn")
1168
  clear_btn = antd.Button("클리어", type="default", size="large", elem_classes="clear-btn")
1169
 
1170
- # ── (3) 입력창 ──
1171
  with antd.Flex(vertical=True, gap="middle", wrap=True, elem_classes="input-panel"):
1172
- # 입력 영역
1173
  input_text = antd.InputTextarea(
1174
  size="large",
1175
  allow_clear=True,
@@ -1178,23 +941,23 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1178
  )
1179
  gr.HTML('<div class="help-text">💡 원하는 게임의 설명을 입력하세요. 예: "테트리스 게임 제작해줘."</div>')
1180
 
1181
- # ── (4) 배포 결과 영역 - JavaScript로 동적 업데이트 ──
1182
- deploy_result_container = gr.HTML("""
1183
- <div class="deploy-section">
1184
- <div class="deploy-header">📤 배포 결과</div>
1185
- <div id="deploy-result-box" class="deploy-result-box">
1186
- <div class="no-deploy">아직 배포된 게임이 없습니다.</div>
 
1187
  </div>
1188
- </div>
1189
- """)
 
 
1190
 
1191
- # JavaScript 실행을 위한 HTML (비출력용)
1192
  js_trigger = gr.HTML(elem_id="js-trigger", visible=False)
1193
 
1194
-
1195
- # ---- 이벤트 / 콜백 ----
1196
-
1197
- # (A) Code Drawer 열기/닫기
1198
  codeBtn.click(
1199
  lambda: gr.update(open=True),
1200
  inputs=[],
@@ -1206,7 +969,7 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1206
  outputs=[code_drawer]
1207
  )
1208
 
1209
- # (B) 히스토리 Drawer 열기/닫기
1210
  historyBtn.click(
1211
  history_render,
1212
  inputs=[history],
@@ -1218,7 +981,7 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1218
  outputs=[history_drawer]
1219
  )
1220
 
1221
- # (C) 템플릿 Drawer 열기/닫기 (하나로 통합)
1222
  template_btn.click(
1223
  fn=lambda: (gr.update(open=True), load_all_templates()),
1224
  outputs=[session_drawer, session_history],
@@ -1233,39 +996,36 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1233
  outputs=[session_drawer, session_history]
1234
  )
1235
 
1236
- # (D) 'Send' 버튼 => 코드 생성
1237
  btn.click(
1238
  demo_instance.generation_code,
1239
  inputs=[input_text, setting, history],
1240
  outputs=[code_output, history, sandbox, state_tab, code_drawer]
1241
  )
1242
 
1243
- # (E) '클리어' 버튼 => 히스토리 초기화
1244
  clear_btn.click(
1245
  demo_instance.clear_history,
1246
  inputs=[],
1247
  outputs=[history]
1248
  )
1249
 
1250
- # (F) 'Boost' 버튼 => 프롬프트 보강
1251
  boost_btn.click(
1252
  fn=handle_boost,
1253
  inputs=[input_text],
1254
  outputs=[input_text, state_tab]
1255
  )
1256
 
1257
- # (G) 'Code실행' 버튼 => 미리보기 iframe 로드
1258
  execute_btn.click(
1259
  fn=execute_code,
1260
  inputs=[input_text],
1261
  outputs=[sandbox, state_tab]
1262
  )
1263
 
1264
- # 수정된 배포 함수
1265
  def deploy_to_vercel(code: str):
1266
- """
1267
- Vercel에 배포하는 함수 - 실제 배포 로직
1268
- """
1269
  try:
1270
  token = "A8IFZmgW2cqA4yUNlLPnci0N" # 실제 토큰 필요
1271
  if not token:
@@ -1281,9 +1041,7 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1281
  "name": project_name,
1282
  "version": "1.0.0",
1283
  "private": True,
1284
- "dependencies": {
1285
- "vite": "^5.0.0"
1286
- },
1287
  "scripts": {
1288
  "dev": "vite",
1289
  "build": "echo 'No build needed' && mkdir -p dist && cp index.html dist/",
@@ -1291,14 +1049,8 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1291
  }
1292
  }
1293
  files = [
1294
- {
1295
- "file": "index.html",
1296
- "data": code
1297
- },
1298
- {
1299
- "file": "package.json",
1300
- "data": json.dumps(package_json, indent=2)
1301
- }
1302
  ]
1303
  project_settings = {
1304
  "buildCommand": "npm run build",
@@ -1327,16 +1079,12 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1327
  except Exception as e:
1328
  return {"status": "error", "message": f"배포 중 오류 발생: {str(e)}"}
1329
 
1330
- # (H) '배포' 버튼 => Vercel - 헤더와 배포 결과 영역 모두 업데이트
1331
  def handle_deploy(code, deploy_status):
1332
  if not code:
1333
- # JavaScript로 오류 표시
1334
  js_code = """
1335
  <script>
1336
- // 헤더 배너에 오류 표시
1337
  showDeployBanner('error', '⚠️ 배포 실패', '배포할 코드가 없습니다. 먼저 게임 코드를 생성해주세요.');
1338
-
1339
- // 결과 영역에도 오류 표시
1340
  document.getElementById('deploy-result-box').innerHTML = `
1341
  <div class="deploy-error">
1342
  <div class="error-icon">⚠️</div>
@@ -1353,13 +1101,9 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1353
  }
1354
 
1355
  try:
1356
- # 로딩 상태 표시
1357
  loading_js = """
1358
  <script>
1359
- // 헤더 배너에 로딩 상태 표시
1360
  showDeployBanner('loading', '🔄 배포 진행 중', 'Vercel에 게임을 배포하고 있습니다. 잠시만 기다려주세요.');
1361
-
1362
- // 결과 영역에도 로딩 표시
1363
  document.getElementById('deploy-result-box').innerHTML = `
1364
  <div class="deploy-loading">
1365
  <div class="loading-spinner"></div>
@@ -1368,25 +1112,16 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1368
  `;
1369
  </script>
1370
  """
1371
- # 로딩 상태를 먼저 업데이트
1372
  yield loading_js, deploy_status
1373
 
1374
- # 코드 전처리
1375
  clean_code = remove_code_block(code)
1376
-
1377
- # 실제 배포 실행
1378
  result = deploy_to_vercel(clean_code)
1379
 
1380
- # 성공 시
1381
  if result.get("status") == "success":
1382
  url = result.get("url")
1383
- # 성공 메시지 JavaScript
1384
  success_js = f"""
1385
  <script>
1386
- // 헤더 배너에 성공 표시 및 URL 포함
1387
  showDeployBanner('success', '✅ 배포 완료!', '게임이 성공적으로 Vercel에 배포되었습니다.', '{url}');
1388
-
1389
- // 결과 영역에도 성공 표시
1390
  document.getElementById('deploy-result-box').innerHTML = `
1391
  <div class="deploy-success">
1392
  <div class="success-icon">✅</div>
@@ -1405,17 +1140,11 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1405
  "url": url,
1406
  "message": "배포 완료!"
1407
  }
1408
-
1409
- # 실패 시
1410
  else:
1411
  error_msg = result.get("message", "알 수 없는 오류")
1412
- # 오류 메시지 JavaScript
1413
  error_js = f"""
1414
  <script>
1415
- // 헤더 배너에 오류 표시
1416
  showDeployBanner('error', '⚠️ 배포 실패', '{error_msg}');
1417
-
1418
- // 결과 영역에도 오류 표시
1419
  document.getElementById('deploy-result-box').innerHTML = `
1420
  <div class="deploy-error">
1421
  <div class="error-icon">⚠️</div>
@@ -1433,13 +1162,9 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1433
 
1434
  except Exception as e:
1435
  error_msg = str(e)
1436
- # 예외 메시지 JavaScript
1437
  exception_js = f"""
1438
  <script>
1439
- // 헤더 배너에 오류 표시
1440
  showDeployBanner('error', '⚠️ 시스템 오류', '{error_msg}');
1441
-
1442
- // 결과 영역에도 오류 표시
1443
  document.getElementById('deploy-result-box').innerHTML = `
1444
  <div class="deploy-error">
1445
  <div class="error-icon">⚠️</div>
@@ -1455,12 +1180,10 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1455
  "url": ""
1456
  }
1457
 
1458
- # 배포 버튼 클릭 - JavaScript 트리거 및 상태 업데이트
1459
-
1460
  deploy_btn.click(
1461
-
1462
  fn=handle_deploy,
1463
- inputs=[code_output, deploy_status], # handle_deploy가 code, deploy_status 2개 받음
1464
  outputs=[deploy_result_container, deploy_status]
1465
  )
1466
 
@@ -1475,4 +1198,4 @@ if __name__ == "__main__":
1475
  demo.queue(default_concurrency_limit=20).launch(ssr_mode=False)
1476
  except Exception as e:
1477
  print(f"Initialization error: {e}")
1478
- raise
 
260
  # ------------------------
261
 
262
  def load_json_data():
 
263
  data_list = [
264
  {
265
  "name": "[게임] 테트리스 클론",
 
269
  "name": "[게임] 체스",
270
  "prompt": "두 명이 번갈아가며 플레이할 수 있는 체스 게임을 만들어주세요. 기본적인 체스 규칙(킹, 퀸, 룩, 비숍, 나이트, 폰의 이동 규칙)을 구현하고, 체크와 체크메이트 감지 기능이 필요합니다. 드래그 앤 드롭으로 말을 움직일 수 있게 하며, 이동 기록도 표시해주세요."
271
  },
272
+ # ... (중략 - 실제 운영 시에는 전체 목록 삽입)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  ]
274
  return data_list
275
 
 
 
 
 
 
 
 
 
276
  def create_template_html(title, items):
277
  """
278
  이미지 없이 템플릿 HTML 생성
 
320
  </style>
321
  <div class="prompt-grid">
322
  """
323
+ import html as html_lib
324
  for item in items:
325
  card_html = f"""
326
+ <div class="prompt-card" onclick="copyToInput(this)" data-prompt="{html_lib.escape(item.get('prompt', ''))}">
327
+ <div class="card-name">{html_lib.escape(item.get('name', ''))}</div>
328
+ <div class="card-prompt">{html_lib.escape(item.get('prompt', ''))}</div>
329
  </div>
330
  """
331
  html_content += card_html
 
357
  # 5) 배포/부스트/기타 유틸
358
  # ------------------------
359
 
 
 
 
 
 
 
 
360
  def remove_code_block(text):
361
  """
362
  More robust function to extract code from markdown code blocks
363
  텍스트에서 ```html 및 ``` 태그를 완전히 제거하는 함수
364
  """
 
365
  pattern = r'```html\s*([\s\S]+?)\s*```'
366
  match = re.search(pattern, text, re.DOTALL)
367
  if match:
368
  return match.group(1).strip()
369
 
 
370
  pattern = r'```(?:\w+)?\s*([\s\S]+?)\s*```'
371
  match = re.search(pattern, text, re.DOTALL)
372
  if match:
373
  return match.group(1).strip()
374
 
 
375
  text = re.sub(r'```html\s*', '', text)
376
  text = re.sub(r'\s*```', '', text)
 
 
377
  return text.strip()
378
 
379
  def optimize_code(code: str) -> str:
 
384
  if not code or len(code.strip()) == 0:
385
  return code
386
 
 
387
  lines = code.split('\n')
388
+ if len(lines) <= 200:
389
  return code
390
 
 
391
  comment_patterns = [
392
+ r'/\*[\s\S]*?\*/',
393
+ r'//.*?$',
394
+ r'<!--[\s\S]*?-->'
395
  ]
 
396
  cleaned_code = code
397
  for pattern in comment_patterns:
398
  cleaned_code = re.sub(pattern, '', cleaned_code, flags=re.MULTILINE)
399
 
 
400
  cleaned_lines = []
401
  empty_line_count = 0
402
  for line in cleaned_code.split('\n'):
403
  if line.strip() == '':
404
  empty_line_count += 1
405
+ if empty_line_count <= 1:
406
  cleaned_lines.append('')
407
  else:
408
  empty_line_count = 0
409
  cleaned_lines.append(line)
410
 
 
411
  cleaned_code = '\n'.join(cleaned_lines)
 
 
412
  cleaned_code = re.sub(r'console\.log\(.*?\);', '', cleaned_code, flags=re.MULTILINE)
 
 
413
  cleaned_code = re.sub(r' {2,}', ' ', cleaned_code)
 
414
  return cleaned_code
415
 
416
  def send_to_sandbox(code):
417
  """
418
+ iframe으로 렌더링
 
419
  """
 
420
  clean_code = remove_code_block(code)
 
 
421
  clean_code = optimize_code(clean_code)
422
 
 
423
  if clean_code.startswith('```html'):
424
  clean_code = clean_code[7:].strip()
425
  if clean_code.endswith('```'):
426
  clean_code = clean_code[:-3].strip()
427
 
 
428
  if not clean_code.strip().startswith('<!DOCTYPE') and not clean_code.strip().startswith('<html'):
429
  clean_code = f"""<!DOCTYPE html>
430
  <html>
 
437
  {clean_code}
438
  </body>
439
  </html>"""
 
 
440
  encoded_html = base64.b64encode(clean_code.encode('utf-8')).decode('utf-8')
441
  data_uri = f"data:text/html;charset=utf-8;base64,{encoded_html}"
442
  return f'<iframe src="{data_uri}" width="100%" height="920px" style="border:none;"></iframe>'
443
 
444
  def boost_prompt(prompt: str) -> str:
 
 
 
445
  if not prompt:
446
  return ""
447
  boost_system_prompt = """당신은 웹 게임 개발 프롬프트 전문가입니다.
 
461
  - 최소한의 필수 게임 요소만 포함
462
  """
463
  try:
464
+ # Claude API
465
  try:
466
  response = claude_client.messages.create(
467
  model="claude-3-7-sonnet-20250219",
468
+ max_tokens=10000,
469
+ temperature=0.3,
470
  messages=[{
471
  "role": "user",
472
  "content": f"다음 게임 프롬프트를 분석하고 증강하되, 간결함을 유지하세요: {prompt}"
 
477
  return response.content[0].text
478
  raise Exception("Claude API 응답 형식 오류")
479
  except Exception:
 
480
  completion = openai_client.chat.completions.create(
481
  model="gpt-4",
482
  messages=[
 
490
  return completion.choices[0].message.content
491
  raise Exception("OpenAI API 응답 형식 오류")
492
  except Exception:
 
493
  return prompt
 
 
494
 
495
  def handle_boost(prompt: str):
496
  try:
 
500
  return prompt, gr.update(active_key="empty")
501
 
502
  def history_render(history: History):
 
 
 
503
  return gr.update(open=True), history
504
 
505
  def execute_code(query: str):
 
 
 
 
506
  if not query or query.strip() == '':
507
  return None, gr.update(active_key="empty")
508
  try:
 
509
  clean_code = remove_code_block(query)
 
 
510
  if clean_code.startswith('```html'):
511
  clean_code = clean_code[7:].strip()
512
  if clean_code.endswith('```'):
513
  clean_code = clean_code[:-3].strip()
 
 
514
  if not clean_code.strip().startswith('<!DOCTYPE') and not clean_code.strip().startswith('<html'):
515
  if not ('<body' in clean_code and '</body>' in clean_code):
516
  clean_code = f"""<!DOCTYPE html>
 
524
  {clean_code}
525
  </body>
526
  </html>"""
 
527
  return send_to_sandbox(clean_code), gr.update(active_key="render")
528
  except Exception as e:
529
  print(f"Execute code error: {str(e)}")
 
545
  if _history is None:
546
  _history = []
547
 
 
548
  query = f"""
549
  다음 게임을 제작해주세요.
550
  중요 요구사항:
 
560
  system_message = messages[0]['content']
561
 
562
  claude_messages = [
563
+ {"role": msg["role"] if msg["role"] != "system" else "user", "content": msg["content"]}
564
  for msg in messages[1:] + [{'role': Role.USER, 'content': query}]
565
  if msg["content"].strip() != ''
566
  ]
 
574
  openai_messages.append({"role": "user", "content": query})
575
 
576
  try:
 
577
  yield [
578
  "Generating code...",
579
  _history,
 
584
  await asyncio.sleep(0)
585
  collected_content = None
586
  try:
 
587
  async for content in try_claude_api(system_message, claude_messages):
588
  yield [
589
  content,
 
595
  await asyncio.sleep(0)
596
  collected_content = content
597
  except Exception:
 
598
  async for content in try_openai_api(openai_messages):
599
  yield [
600
  content,
 
605
  ]
606
  await asyncio.sleep(0)
607
  collected_content = content
608
+
609
  if collected_content:
 
610
  clean_code = remove_code_block(collected_content)
611
  code_lines = clean_code.count('\n') + 1
612
+ if code_lines > 700:
 
613
  warning_msg = f"""
614
  ⚠️ **경고: 생성된 코드가 너무 깁니다 ({code_lines}줄)**
615
  이로 인해 실행 시 오류가 발생할 수 있습니다. 다음과 같이 시도해 보세요:
 
619
  ```html
620
  {clean_code[:2000]}
621
  ... (코드가 너무 깁니다) ... """
 
622
  collected_content = warning_msg
 
623
  yield [
624
  collected_content,
625
  _history,
 
628
  gr.update(open=True)
629
  ]
630
  else:
 
631
  _history = messages_to_history([
632
  {'role': Role.SYSTEM, 'content': system_message}
633
  ] + claude_messages + [{
634
  'role': Role.ASSISTANT,
635
  'content': collected_content
636
  }])
 
637
  yield [
638
  collected_content,
639
  _history,
 
665
  )
666
 
667
  with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
668
+ # 헤더 HTML
669
  header_html = gr.HTML("""
670
  <div class="app-header">
671
  <h1>🎮 Vibe Game Craft</h1>
 
821
  const url = document.getElementById('deploy-banner-url').href;
822
  navigator.clipboard.writeText(url)
823
  .then(() => {
 
824
  const copyBtn = document.querySelector('.deploy-banner-copy-btn');
825
  const originalText = copyBtn.textContent;
826
  copyBtn.textContent = '복사됨!';
 
 
827
  setTimeout(() => {
828
  copyBtn.textContent = originalText;
829
  }, 1000);
 
838
  const bannerUrlContainer = document.getElementById('deploy-banner-url-container');
839
  const bannerUrl = document.getElementById('deploy-banner-url');
840
 
 
841
  banner.className = 'deploy-banner ' + type;
 
 
842
  bannerTitle.textContent = title;
843
  bannerMessage.textContent = message;
844
 
 
845
  if (url) {
846
  bannerUrl.href = url;
847
  bannerUrl.textContent = url;
 
850
  bannerUrlContainer.style.display = 'none';
851
  }
852
 
 
853
  banner.style.display = 'block';
854
  }
855
  </script>
 
858
  history = gr.State([])
859
  setting = gr.State({"system": SystemPrompt})
860
 
861
+ # 배포 상태를 저장할 변수
862
  deploy_status = gr.State({
863
  "is_deployed": False,
864
  "status": "",
 
879
  show_label=False, flushing=False, height=960, elem_classes="history_chatbot"
880
  )
881
 
882
+ # templates Drawer
883
  with antd.Drawer(
884
  open=False,
885
  title="게임 템플릿",
 
892
  session_history = gr.HTML(elem_classes="session-history")
893
  close_btn = antd.Button("닫기", type="default", elem_classes="close-btn")
894
 
895
+ # 좌우 레이아웃
896
  with antd.Row(gutter=[32, 12], align="top", elem_classes="equal-height-container") as layout:
897
 
898
+ # 왼쪽 Col
899
  with antd.Col(span=24, md=16, elem_classes="equal-height-col"):
900
  with ms.Div(elem_classes="right_panel panel"):
901
  gr.HTML(r"""
 
915
  with antd.Tabs.Item(key="render"):
916
  sandbox = gr.HTML(elem_classes="html_content")
917
 
918
+ # 오른쪽 Col
919
  with antd.Col(span=24, md=8, elem_classes="equal-height-col"):
 
920
  with antd.Flex(vertical=True, gap="small", elem_classes="right-top-buttons"):
921
+ # 상단 메뉴 버튼들
922
  with antd.Flex(gap="small", elem_classes="setting-buttons", justify="space-between"):
923
  codeBtn = antd.Button("🧑‍💻 코드 보기", type="default", elem_classes="code-btn")
924
  historyBtn = antd.Button("📜 히스토리", type="default", elem_classes="history-btn")
925
  template_btn = antd.Button("🎮 템플릿", type="default", elem_classes="template-btn")
926
 
927
+ # 액션 버튼들
928
  with antd.Flex(gap="small", justify="space-between", elem_classes="action-buttons"):
929
  btn = antd.Button("전송", type="primary", size="large", elem_classes="send-btn")
930
  boost_btn = antd.Button("증강", type="default", size="large", elem_classes="boost-btn")
 
932
  deploy_btn = antd.Button("배포", type="default", size="large", elem_classes="deploy-btn")
933
  clear_btn = antd.Button("클리어", type="default", size="large", elem_classes="clear-btn")
934
 
 
935
  with antd.Flex(vertical=True, gap="middle", wrap=True, elem_classes="input-panel"):
 
936
  input_text = antd.InputTextarea(
937
  size="large",
938
  allow_clear=True,
 
941
  )
942
  gr.HTML('<div class="help-text">💡 원하는 게임의 설명을 입력하세요. 예: "테트리스 게임 제작해줘."</div>')
943
 
944
+ deploy_result_container = gr.HTML(
945
+ """
946
+ <div class="deploy-section">
947
+ <div class="deploy-header">📤 배포 결과</div>
948
+ <div id="deploy-result-box" class="deploy-result-box">
949
+ <div class="no-deploy">아직 배포된 게임이 없습니다.</div>
950
+ </div>
951
  </div>
952
+ """,
953
+ # 스크립트를 렌더링하려면 unsafe_allow_html=True 필요
954
+ unsafe_allow_html=True
955
+ )
956
 
 
957
  js_trigger = gr.HTML(elem_id="js-trigger", visible=False)
958
 
959
+ # 이벤트 / 콜백
960
+ # Code Drawer
 
 
961
  codeBtn.click(
962
  lambda: gr.update(open=True),
963
  inputs=[],
 
969
  outputs=[code_drawer]
970
  )
971
 
972
+ # History Drawer
973
  historyBtn.click(
974
  history_render,
975
  inputs=[history],
 
981
  outputs=[history_drawer]
982
  )
983
 
984
+ # Template Drawer
985
  template_btn.click(
986
  fn=lambda: (gr.update(open=True), load_all_templates()),
987
  outputs=[session_drawer, session_history],
 
996
  outputs=[session_drawer, session_history]
997
  )
998
 
999
+ # 전송 버튼
1000
  btn.click(
1001
  demo_instance.generation_code,
1002
  inputs=[input_text, setting, history],
1003
  outputs=[code_output, history, sandbox, state_tab, code_drawer]
1004
  )
1005
 
1006
+ # 클리어 버튼
1007
  clear_btn.click(
1008
  demo_instance.clear_history,
1009
  inputs=[],
1010
  outputs=[history]
1011
  )
1012
 
1013
+ # 증강 버튼
1014
  boost_btn.click(
1015
  fn=handle_boost,
1016
  inputs=[input_text],
1017
  outputs=[input_text, state_tab]
1018
  )
1019
 
1020
+ # 코드 실행 버튼
1021
  execute_btn.click(
1022
  fn=execute_code,
1023
  inputs=[input_text],
1024
  outputs=[sandbox, state_tab]
1025
  )
1026
 
1027
+ # 실제 배포 로직
1028
  def deploy_to_vercel(code: str):
 
 
 
1029
  try:
1030
  token = "A8IFZmgW2cqA4yUNlLPnci0N" # 실제 토큰 필요
1031
  if not token:
 
1041
  "name": project_name,
1042
  "version": "1.0.0",
1043
  "private": True,
1044
+ "dependencies": {"vite": "^5.0.0"},
 
 
1045
  "scripts": {
1046
  "dev": "vite",
1047
  "build": "echo 'No build needed' && mkdir -p dist && cp index.html dist/",
 
1049
  }
1050
  }
1051
  files = [
1052
+ {"file": "index.html", "data": code},
1053
+ {"file": "package.json", "data": json.dumps(package_json, indent=2)}
 
 
 
 
 
 
1054
  ]
1055
  project_settings = {
1056
  "buildCommand": "npm run build",
 
1079
  except Exception as e:
1080
  return {"status": "error", "message": f"배포 중 오류 발생: {str(e)}"}
1081
 
1082
+ # handle_deploy
1083
  def handle_deploy(code, deploy_status):
1084
  if not code:
 
1085
  js_code = """
1086
  <script>
 
1087
  showDeployBanner('error', '⚠️ 배포 실패', '배포할 코드가 없습니다. 먼저 게임 코드를 생성해주세요.');
 
 
1088
  document.getElementById('deploy-result-box').innerHTML = `
1089
  <div class="deploy-error">
1090
  <div class="error-icon">⚠️</div>
 
1101
  }
1102
 
1103
  try:
 
1104
  loading_js = """
1105
  <script>
 
1106
  showDeployBanner('loading', '🔄 배포 진행 중', 'Vercel에 게임을 배포하고 있습니다. 잠시만 기다려주세요.');
 
 
1107
  document.getElementById('deploy-result-box').innerHTML = `
1108
  <div class="deploy-loading">
1109
  <div class="loading-spinner"></div>
 
1112
  `;
1113
  </script>
1114
  """
 
1115
  yield loading_js, deploy_status
1116
 
 
1117
  clean_code = remove_code_block(code)
 
 
1118
  result = deploy_to_vercel(clean_code)
1119
 
 
1120
  if result.get("status") == "success":
1121
  url = result.get("url")
 
1122
  success_js = f"""
1123
  <script>
 
1124
  showDeployBanner('success', '✅ 배포 완료!', '게임이 성공적으로 Vercel에 배포되었습니다.', '{url}');
 
 
1125
  document.getElementById('deploy-result-box').innerHTML = `
1126
  <div class="deploy-success">
1127
  <div class="success-icon">✅</div>
 
1140
  "url": url,
1141
  "message": "배포 완료!"
1142
  }
 
 
1143
  else:
1144
  error_msg = result.get("message", "알 수 없는 오류")
 
1145
  error_js = f"""
1146
  <script>
 
1147
  showDeployBanner('error', '⚠️ 배포 실패', '{error_msg}');
 
 
1148
  document.getElementById('deploy-result-box').innerHTML = `
1149
  <div class="deploy-error">
1150
  <div class="error-icon">⚠️</div>
 
1162
 
1163
  except Exception as e:
1164
  error_msg = str(e)
 
1165
  exception_js = f"""
1166
  <script>
 
1167
  showDeployBanner('error', '⚠️ 시스템 오류', '{error_msg}');
 
 
1168
  document.getElementById('deploy-result-box').innerHTML = `
1169
  <div class="deploy-error">
1170
  <div class="error-icon">⚠️</div>
 
1180
  "url": ""
1181
  }
1182
 
1183
+ # 배포 버튼 클릭
 
1184
  deploy_btn.click(
 
1185
  fn=handle_deploy,
1186
+ inputs=[code_output, deploy_status],
1187
  outputs=[deploy_result_container, deploy_status]
1188
  )
1189
 
 
1198
  demo.queue(default_concurrency_limit=20).launch(ssr_mode=False)
1199
  except Exception as e:
1200
  print(f"Initialization error: {e}")
1201
+ raise