Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import random | |
| import json | |
| import base64 | |
| import io | |
| import os | |
| from PIL import Image, ImageDraw, ImageFont | |
| from google import genai | |
| from google.genai import types | |
| API_KEY = os.environ.get("GOOGLE_API_KEY") | |
| client = genai.Client(api_key=API_KEY) | |
| class AIAdventureGame: | |
| def __init__(self): | |
| """ๅๅงๅ้ๆฒ็ณป็ตฑ""" | |
| self.player = None | |
| self.backpack = [] | |
| self.game_history = [] | |
| self.current_scene = "" | |
| self.current_riddle = None # ๆฐๅข๏ผ็ถๅ่ฌ่ช | |
| self.current_answer = None # ๆฐๅข๏ผ็ถๅ่ฌ่ช็ญๆก | |
| def create_character(self, name): | |
| """ๅตๅปบ็ฉๅฎถ่ง่ฒ""" | |
| if not name.strip(): | |
| name = "ๅ้ช่ " | |
| self.player = { | |
| "name": name, | |
| "HP": 100, | |
| "max_HP": 100, | |
| "Attack": 15, | |
| "Defense": 10, | |
| "Gold": 50, | |
| "level": 1, | |
| "exp": 0 | |
| } | |
| self.backpack = ["ๆฒป็่ฅๆฐด"] | |
| self.game_history = [f"ๆญก่ฟ๏ผๅๆข็{name}๏ผไฝ ็ๅ้ชๅณๅฐ้ๅง๏ผ"] | |
| return self.get_status_text(), self.get_history_text() | |
| def get_status_text(self): | |
| """็ฒๅ็ฉๅฎถ็ๆ ๆๅญ""" | |
| if not self.player: | |
| return "่ซๅ ๅตๅปบ่ง่ฒ" | |
| status = f""" | |
| ใ่ง่ฒ็ๆ ใ | |
| ๅงๅ: {self.player['name']} (็ญ็ด {self.player['level']}) | |
| ็ๅฝๅผ: {self.player['HP']}/{self.player['max_HP']} | |
| ๆปๆๅ: {self.player['Attack']} | |
| ้ฒ็ฆฆๅ: {self.player['Defense']} | |
| ้ๅนฃ: {self.player['Gold']} | |
| ็ถ้ฉๅผ: {self.player['exp']}/100 | |
| ใ่ๅ ็ฉๅใ | |
| {', '.join(self.backpack) if self.backpack else '็ฉบ็'} | |
| """ | |
| return status.strip() | |
| def get_history_text(self): | |
| """็ฒๅ้ๆฒๆญทๅฒๆๅญ""" | |
| return "\n".join(self.game_history[-10:]) # ๅช้กฏ็คบๆ่ฟ 10 ็ญ่จ้ | |
| def generate_ai_scene(self, scene_type="explore"): | |
| """ไฝฟ็จAI็ๆๅ ดๆฏๆ่ฟฐ""" | |
| try: | |
| prompts = { | |
| "explore": f"ไฝ ๆฏไธๅRPG้ๆฒ็ๆ่ฟฐ่ ใ{self.player['name']}ๆญฃๅจ้ฒ่กๅ้ชๆข็ดขใ่ซ็ๆไธๅๆ่ถฃ็ๆข็ดขๅ ดๆฏๆ่ฟฐ๏ผ50-100ๅญ๏ผ๏ผๅ ๅซ็ฐๅขใๆฐๅๅๅฏ่ฝ็็ผ็พใๅ ดๆฏๆ่ฉฒ้ฉๅ็ญ็ด{self.player['level']}็ๅ้ช่ ใ", | |
| "battle": f"็ๆไธๅๆฐ้ฌฅๅ ดๆฏ็้ๅ ดๆ่ฟฐ๏ผ30-50ๅญ๏ผ๏ผ{self.player['name']}้ญ้ไบๅฑ้ชใ", | |
| "treasure": f"็ๆไธๅ็ผ็พๅฏถ่็ๅ ดๆฏๆ่ฟฐ๏ผ30-50ๅญ๏ผ๏ผ{self.player['name']}ๆพๅฐไบๅฅฝๆฑ่ฅฟใ", | |
| "trap": f"็ๆไธๅ้ท้ฑๅ ดๆฏๆ่ฟฐ๏ผ30-50ๅญ๏ผ๏ผ{self.player['name']}้ๅฐไบๅฑ้ชใ" | |
| } | |
| response = client.models.generate_content( | |
| model="gemini-2.0-flash-001", | |
| contents=prompts.get(scene_type, prompts["explore"]), | |
| config=types.GenerateContentConfig( | |
| safety_settings=[ | |
| types.SafetySetting( | |
| category=types.HarmCategory.HARM_CATEGORY_HATE_SPEECH, | |
| threshold=types.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, | |
| ), | |
| types.SafetySetting( | |
| category=types.HarmCategory.HARM_CATEGORY_HARASSMENT, | |
| threshold=types.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, | |
| ), | |
| types.SafetySetting( | |
| category=types.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, | |
| threshold=types.HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, | |
| ), | |
| ] | |
| ) | |
| ) | |
| return response.text.strip() | |
| except Exception as e: | |
| # ๅฆๆAI็ๆๅคฑๆ๏ผไฝฟ็จ้ ่จญๆๅญ | |
| default_scenes = { | |
| "explore": "ไฝ ่ตฐๅจไธๆข่ฟ่็ๅฐๅพไธ๏ผ้ฝๅ ้้ๆจน่็ไธๆ้ง็ๅ ๅฝฑใ", | |
| "battle": "็ช็ถ๏ผไธๅๆตไบบๅพ้ฐๅฝฑไธญ่ทณๅบไพ๏ผ", | |
| "treasure": "ไฝ ๅจ่ง่ฝ็ผ็พไบไธๅ้้็ผๅ ็ๅฏถ็ฎฑ๏ผ", | |
| "trap": "็ณ็ณ๏ผไฝ ่ธฉๅฐไบไธๅ้ฑ่็้ท้ฑ๏ผ" | |
| } | |
| return default_scenes.get(scene_type, default_scenes["explore"]) | |
| def generate_ai_monster(self): | |
| """ไฝฟ็จAI็ๆๆช็ฉ""" | |
| try: | |
| level = self.player['level'] | |
| prompt = f"""็ๆไธๅ้ฉๅ็ญ็ด{level}ๅ้ช่ ็RPGๆช็ฉ๏ผไปฅJSONๆ ผๅผๅๆ๏ผ | |
| {{ | |
| "name": "ๆช็ฉๅ็จฑ", | |
| "description": "30ๅญไปฅๅ ง็ๆ่ฟฐ", | |
| "HP": ๅบ็ค็ๅฝๅผ, | |
| "Attack": ๆปๆๅ, | |
| "Defense": ้ฒ็ฆฆๅ, | |
| "weakness": "ๅผฑ้ปๆ็นๆฎ่ชชๆ" | |
| }} | |
| ๆช็ฉๅผทๅบฆๆ่ฉฒ่็ฉๅฎถ็ญ็ดๅน้ ใ""" | |
| response = client.models.generate_content( | |
| model="gemini-2.0-flash-001", | |
| contents=prompt | |
| ) | |
| monster_data = json.loads(response.text.strip()) | |
| # ๆ นๆ็ญ็ด่ชฟๆดๆธๅผ | |
| level_multiplier = 1 + (level - 1) * 0.3 | |
| monster_data["HP"] = int(monster_data["HP"] * level_multiplier) | |
| monster_data["Attack"] = int(monster_data["Attack"] * level_multiplier) | |
| monster_data["Defense"] = int(monster_data["Defense"] * level_multiplier) | |
| return monster_data | |
| except Exception as e: | |
| # ้ ่จญๆช็ฉ | |
| monsters = [ | |
| {"name": "ๅฅๅธๆ", "description": "็ถ ่ฒ็ๅฐๆก้ญ", "HP": 40, "Attack": 8, "Defense": 3, "weakness": "ๅฎณๆๅ ไบฎ"}, | |
| {"name": "้ชท้ซๆฐๅฃซ", "description": "ไธๆญป็ๆฐๅฃซ", "HP": 60, "Attack": 12, "Defense": 8, "weakness": "็ฅ่ๆปๆๆๆ"}, | |
| {"name": "้็ผ", "description": "ๅ ็็้็ธ", "HP": 45, "Attack": 15, "Defense": 5, "weakness": "็ซ็ฐๅทๅฎณๅ ๅ"} | |
| ] | |
| return random.choice(monsters) | |
| def generate_scene_image(self, scene_description): | |
| """ไฝฟ็จGemini 2.0 Flash็ๆๅ ดๆฏๆ็ซ""" | |
| try: | |
| # ๆ นๆๅ ดๆฏๆ่ฟฐๆงๅปบ่ฉณ็ดฐ็ๅๅ็ๆๆ็คบ่ฉ | |
| scene_keywords = self.extract_scene_keywords(scene_description) | |
| image_prompt = f""" | |
| ่ซ็ๆไธๅน ็ฒพ็พ็ๅฅๅนป่ง่ฒๆฎๆผ้ๆฒๆ็ซ๏ผๅ ดๆฏๆ่ฟฐ๏ผ{scene_description} | |
| ็ซ้ขจ่่ฆๆ ผ๏ผ | |
| - ๅฅๅนปๅ้ช้ๆฒ็พ่ก้ขจๆ ผ | |
| - ่ฒๅฝฉ้ฎฎ่ฑใๅ ๆปฟ้ญๅนปๆฐๅ | |
| - ๅๆผซ๏ผๆผซ็ซๅ็ผ็ๆ็ซๆๆณ | |
| - ๅฐๆฅญ็ดๆธไฝ็นช็ซๅ่ณช | |
| - ้ปๅฝฑ็ดๅ ๅฝฑ่ๆงๅ | |
| - 16:9 ๆฉซๅ็ซ้ขๆฏไพ | |
| - ๅ ทๅๆฏๆทฑ่ๅคงๆฐฃ้่ฆๆๆ | |
| ๅ ดๆฏๅ ็ด ๏ผ**ๅฟ ้ ๅ ๅซ**๏ผ๏ผ | |
| {scene_keywords} | |
| ๆ ็ท่จญๅฎ๏ผๅ ๆปฟๅ้ช่้ญๆณๆ | |
| ๅ่ณช่ฆๆฑ๏ผๅฐๆฅญ้ๆฒ็พ่กๆ็ซ | |
| **ๅดๆ ผ็ฆๆญขๅบ็พ**๏ผ | |
| - ไปปไฝๆๅญใๅญๆฏใๅฎ่ฉใๅญๅ | |
| - ไปปไฝ็ฌฆ่ใๆจ่ชใๅ็คบ | |
| - ๆตฎๆฐดๅฐใ็ฐฝๅใๅๆจใLogo | |
| - ๆจ้กใๆฉซๅน ใไป้ขๅ ็ด ๅไปปไฝๆๅญ็ธ้ๅ งๅฎน | |
| """ | |
| # ไฝฟ็จ Gemini 2.0 Flash ็ๆๅๅ | |
| print("๐จ ๆญฃๅจ็ๆAIๆ็ซ...") | |
| response = client.models.generate_content( | |
| model="gemini-2.0-flash-preview-image-generation", | |
| contents=image_prompt, | |
| config=types.GenerateContentConfig( | |
| response_modalities=['TEXT', 'IMAGE'] | |
| ) | |
| ) | |
| # ่็็ๆ็ๅๅ | |
| if self.extract_image_from_response(response): | |
| print("โ AIๆ็ซ็ๆๆๅ๏ผ") | |
| return self.extract_image_from_response(response) | |
| else: | |
| print("โ ๏ธ ็กๆณๅพAIๅๆไธญๆๅๅๅ๏ผไฝฟ็จๅ็จๅๅ") | |
| return self.create_fallback_image(scene_description) | |
| except Exception as e: | |
| print(f"๐ซ AIๅๅ็ๆๅคฑๆ: {str(e)}") | |
| print("๐ ไฝฟ็จๆ็นช้ขจๆ ผๅ็จๅๅ") | |
| return self.create_fallback_image(scene_description) | |
| def extract_scene_keywords(self, scene_description): | |
| """ๅพๅ ดๆฏๆ่ฟฐไธญๆๅ้้ต่ฉไพๅชๅๅๅ็ๆ""" | |
| keywords = [] | |
| prompt = f"""ไฝ ๆฏไธไฝไธๅ้ๆฒ็พ่ก่จญ่จๅฉ็ใ่ซๆ นๆไปฅไธ็ๅ ดๆฏๆ่ฟฐ๏ผๆๅๆๆ้้ต็่ฆ่ฆบ่ๆฐๅ็นๅพต๏ผ่ฎๅฎๅๆขๅ ท้ซๅ่ฑๅฏ๏ผ็ฌฆๅๅฅๅนปๅ้ช้ๆฒๆ็ซ็้ๆฑใ | |
| - ่ซ่ณๅฐๅๅบ 5๏ฝ8 ๅ่ฆ็ด ๏ผๆฏๅ่ฆ็ด ้ฝ่ฆ็ฐกๆฝๅฐ้ปๅบ็ซ้ขไธญๅฏ่ขซๅผทๅๆ็ชๅบ็็ดฐ็ฏใ | |
| - ่ซ็จใ- ่ฆ็ด : ๆ่ฟฐใ็ๆ ผๅผ่ผธๅบ๏ผไธ่ฆๆๅ ถไปๅค้คๆๅญๆ่ชชๆใ | |
| ๅ ดๆฏๆ่ฟฐ๏ผ | |
| {scene_description}""" | |
| response = client.models.generate_content( | |
| model="gemini-2.0-flash-001", | |
| contents=prompt | |
| ) | |
| keywords = response.text | |
| return keywords | |
| def extract_image_from_response(self, response): | |
| """ๅพGeminiๅๆไธญๆๅๅๅ""" | |
| try: | |
| # ๆชขๆฅๅๆๆฏๅฆๅ ๅซๅๅๆธๆ | |
| if hasattr(response, 'parts') and response.parts: | |
| for part in response.parts: | |
| if hasattr(part, 'inline_data') and part.inline_data: | |
| # ่งฃ็ขผbase64ๅๅๆธๆ | |
| if hasattr(part.inline_data, 'data'): | |
| image_data = part.inline_data.data | |
| img = Image.open(io.BytesIO(image_data)) | |
| # ่ชฟๆดๅๅๅคงๅฐไปฅ้ฉๅ็้ข | |
| img = img.resize((512, 384), Image.Resampling.LANCZOS) | |
| return img | |
| # ๅ่ฉฆๅ ถไปๅฏ่ฝ็ๅๅๆธๆๆ ผๅผ | |
| if hasattr(response, 'candidates') and response.candidates: | |
| for candidate in response.candidates: | |
| if hasattr(candidate, 'content') and candidate.content: | |
| for part in candidate.content.parts: | |
| if hasattr(part, 'inline_data') and part.inline_data: | |
| image_data = part.inline_data.data | |
| img = Image.open(io.BytesIO(image_data)) | |
| img = img.resize((512, 384), Image.Resampling.LANCZOS) | |
| return img | |
| return None | |
| except Exception as e: | |
| print(f"ๅๅๆๅ้ฏ่ชค: {str(e)}") | |
| return None | |
| def create_fallback_image(self, scene_description): | |
| """ๅตๅปบๅ็จๅ ดๆฏๅ็๏ผ็ถAIๅๅ็ๆๅคฑๆๆไฝฟ็จ๏ผ""" | |
| try: | |
| # ๅตๅปบไธๅๆด็ฒพ็พ็ๆ็นช้ขจๆ ผๅ ดๆฏๅ็ | |
| img = Image.new('RGB', (512, 384), color=(135, 206, 250)) # ๅคฉ็ฉบ่่ๆฏ | |
| draw = ImageDraw.Draw(img) | |
| # ๆ นๆๅ ดๆฏๆ่ฟฐ้ธๆไธๅ็็นช่ฃฝ้ขจๆ ผ | |
| if any(word in scene_description for word in ["ๆฃฎๆ", "ๆจน", "ๅขๆ", "ๆจนๆจ"]): | |
| # ๆฃฎๆๅ ดๆฏ | |
| self.draw_forest_scene(draw, img.size) | |
| elif any(word in scene_description for word in ["ๅฑฑ", "ๅฒฉ็ณ", "ๅณญๅฃ", "้ซๅ"]): | |
| # ๅฑฑๅฐๅ ดๆฏ | |
| self.draw_mountain_scene(draw, img.size) | |
| elif any(word in scene_description for word in ["ๆด็ฉด", "ๅฐไธ", "ๆด็ช", "ๅฒฉๆด"]): | |
| # ๆด็ฉดๅ ดๆฏ | |
| self.draw_cave_scene(draw, img.size) | |
| elif any(word in scene_description for word in ["ๅๅ ก", "ๅปบ็ฏ", "ๅปขๅข", "้บ่ทก"]): | |
| # ๅปบ็ฏๅ ดๆฏ | |
| self.draw_castle_scene(draw, img.size) | |
| else: | |
| # ้ป่ช่ๅๅ ดๆฏ | |
| self.draw_default_scene(draw, img.size) | |
| # ๆทปๅ ่ฃ้ฃพๆงๆๅญ | |
| try: | |
| font = ImageFont.load_default() | |
| # ๅจๅ็ๅบ้จๆทปๅ ๅ้ๆ่ๆฏ | |
| draw.rectangle([0, 340, 512, 384], fill=(0, 0, 0, 128)) | |
| draw.text((10, 350), f"๐จ ๅ้ชๅ ดๆฏ: {scene_description[:30]}...", | |
| fill=(255, 255, 255), font=font) | |
| except: | |
| pass | |
| return img | |
| except Exception as e: | |
| # ๆ็ตๅ็จๆนๆก๏ผ็ด่ฒ่ๆฏ | |
| img = Image.new('RGB', (512, 384), color=(100, 149, 237)) | |
| draw = ImageDraw.Draw(img) | |
| draw.text((10, 10), "ๅ ดๆฏ็ๆไธญ...", fill=(255, 255, 255)) | |
| return img | |
| def draw_forest_scene(self, draw, size): | |
| """็นช่ฃฝๆฃฎๆๅ ดๆฏ""" | |
| width, height = size | |
| # ็นช่ฃฝๅคๅฑคๆจนๆจ | |
| tree_positions = [(80, 250), (200, 230), (320, 270), (450, 240)] | |
| tree_colors = [(34, 139, 34), (0, 128, 0), (46, 125, 50), (27, 94, 32)] | |
| for i, (x, y) in enumerate(tree_positions): | |
| color = tree_colors[i % len(tree_colors)] | |
| # ๆจนๅ | |
| draw.ellipse([x-30, y-50, x+30, y], fill=color) | |
| # ๆจนๅนน | |
| draw.rectangle([x-8, y, x+8, y+50], fill=(101, 67, 33)) | |
| # ็นช่ฃฝ่ๅฐ | |
| draw.rectangle([0, height-80, width, height], fill=(76, 175, 80)) | |
| # ๆทปๅ ๅฐ่ๆๆ | |
| for i in range(20): | |
| x = random.randint(0, width) | |
| y = random.randint(height-50, height-10) | |
| draw.line([(x, y), (x+2, y-8)], fill=(27, 94, 32), width=2) | |
| def draw_mountain_scene(self, draw, size): | |
| """็นช่ฃฝๅฑฑๅฐๅ ดๆฏ""" | |
| width, height = size | |
| # ็นช่ฃฝ้ ๅฑฑ | |
| draw.polygon([(0, height//2), (width//4, height//4), (width//2, height//3), | |
| (3*width//4, height//5), (width, height//3), (width, height), (0, height)], | |
| fill=(105, 105, 105)) | |
| # ็นช่ฃฝ่ฟๅฑฑ | |
| draw.polygon([(0, 2*height//3), (width//3, height//2), (2*width//3, 3*height//5), | |
| (width, 2*height//3), (width, height), (0, height)], | |
| fill=(69, 90, 100)) | |
| # ๅฑฑ้ ็ฉ้ช | |
| draw.polygon([(width//4, height//4), (width//4-20, height//4+20), | |
| (width//4+20, height//4+20)], fill=(255, 255, 255)) | |
| def draw_cave_scene(self, draw, size): | |
| """็นช่ฃฝๆด็ฉดๅ ดๆฏ""" | |
| width, height = size | |
| # ้ปๆ่ๆฏ | |
| draw.rectangle([0, 0, width, height], fill=(30, 30, 30)) | |
| # ๆด็ฉดๅ ฅๅฃ | |
| draw.ellipse([width//4, height//3, 3*width//4, 2*height//3], fill=(60, 60, 60)) | |
| draw.ellipse([width//3, height//2-20, 2*width//3, height//2+60], fill=(20, 20, 20)) | |
| # ๅฒฉ็ณ | |
| for i in range(5): | |
| x = random.randint(0, width-50) | |
| y = random.randint(height-100, height-20) | |
| draw.ellipse([x, y, x+40, y+30], fill=(80, 80, 80)) | |
| def draw_castle_scene(self, draw, size): | |
| """็นช่ฃฝๅๅ กๅ ดๆฏ""" | |
| width, height = size | |
| # ๅๅ กไธป้ซ | |
| castle_x = width//3 | |
| castle_width = width//3 | |
| castle_height = height//2 | |
| draw.rectangle([castle_x, height-castle_height, castle_x+castle_width, height], | |
| fill=(139, 69, 19)) | |
| # ๅๅ กๅกๆจ | |
| for i in range(3): | |
| tower_x = castle_x + i * castle_width//3 | |
| draw.rectangle([tower_x, height-castle_height-40, tower_x+30, height-castle_height], | |
| fill=(160, 82, 45)) | |
| # ๅก้ | |
| draw.polygon([(tower_x, height-castle_height-40), | |
| (tower_x+15, height-castle_height-60), | |
| (tower_x+30, height-castle_height-40)], fill=(139, 0, 0)) | |
| def draw_default_scene(self, draw, size): | |
| """็นช่ฃฝ้ป่ช่ๅๅ ดๆฏ""" | |
| width, height = size | |
| # ่ๅฐ | |
| draw.rectangle([0, 2*height//3, width, height], fill=(76, 175, 80)) | |
| # ้ ่็ไธ้ต | |
| draw.ellipse([-50, height//2, width//3, 2*height//3], fill=(46, 125, 50)) | |
| draw.ellipse([2*width//3, height//2+20, width+50, 2*height//3+30], fill=(46, 125, 50)) | |
| # ้ฒๆต | |
| cloud_positions = [(100, 50), (300, 80), (450, 40)] | |
| for x, y in cloud_positions: | |
| for i in range(3): | |
| draw.ellipse([x+i*15-10, y-5, x+i*15+25, y+15], fill=(255, 255, 255)) | |
| def explore(self): | |
| """ๆข็ดขๅ่ฝ""" | |
| if not self.player or self.player['HP'] <= 0: | |
| return "่ซๅ ๅตๅปบ่ง่ฒๆ่ง่ฒๅทฒๆญปไบก", "", None | |
| # ็ๆ้จๆฉไบไปถ | |
| event = random.choice(["battle", "treasure", "trap", "peaceful"]) | |
| scene_description = self.generate_ai_scene("explore") | |
| result_text = f"๐ {scene_description}\n\n" | |
| if event == "battle": | |
| monster = self.generate_ai_monster() | |
| battle_scene = self.generate_ai_scene("battle") | |
| scene_description += f"\n\n{battle_scene}" | |
| result_text += f"โ๏ธ {battle_scene}\n" | |
| result_text += f"ไฝ ้ๅฐไบ: {monster['name']}\n" | |
| result_text += f"ๆ่ฟฐ: {monster['description']}\n" | |
| result_text += f"ๅผฑ้ป: {monster['weakness']}\n" | |
| result_text += f"็ๅฝๅผ: {monster['HP']}, ๆปๆ: {monster['Attack']}, ้ฒ็ฆฆ: {monster['Defense']}\n\n" | |
| # ๅท่กๆฐ้ฌฅ | |
| battle_result = self.battle(monster) | |
| result_text += f"ๆฐ้ฌฅ็ตๆ: {battle_result}" | |
| elif event == "treasure": | |
| treasure_scene = self.generate_ai_scene("treasure") | |
| scene_description += f"\n\n{treasure_scene}" | |
| result_text += f"๐ฐ {treasure_scene}\n" | |
| treasure_type = random.choice(["gold", "healing_potion", "equipment"]) # Use healing_potion key | |
| if treasure_type == "gold": | |
| gold_amount = random.randint(20, 50) | |
| self.player["Gold"] += gold_amount | |
| result_text += f"ไฝ ็ฒๅพไบ {gold_amount} ้ๅนฃ๏ผ" | |
| elif treasure_type == "healing_potion": # Use healing_potion key | |
| self.backpack.append("ๆฒป็่ฅๆฐด") # Keep Chinese name in backpack | |
| result_text += "ไฝ ็ฒๅพไบไธ็ถๆฒป็่ฅๆฐด๏ผ" | |
| else: | |
| if random.choice([True, False]): | |
| self.player["Attack"] += random.randint(1, 3) | |
| result_text += f"ไฝ ๆพๅฐไบไธๆๆญฆๅจ๏ผๆปๆๅๅขๅ ๏ผ" | |
| else: | |
| self.player["Defense"] += random.randint(1, 2) | |
| result_text += f"ไฝ ๆพๅฐไบไธไปถ้ฒๅ ท๏ผ้ฒ็ฆฆๅๅขๅ ๏ผ" | |
| elif event == "trap": | |
| trap_scene = self.generate_ai_scene("trap") | |
| scene_description += f"\n\n{trap_scene}" | |
| result_text += f"๐ {trap_scene}\n" | |
| damage = random.randint(10, 25) | |
| self.player["HP"] = max(0, self.player["HP"] - damage) | |
| result_text += f"ไฝ ๅๅฐไบ {damage} ้ปๅทๅฎณ๏ผ" | |
| else: # peaceful | |
| result_text += "๐ธ ้่ฃกๅพๅฏง้๏ผไฝ ๆขๅพฉไบไธไบ้ซๅใ" | |
| self.player["HP"] = min(self.player["max_HP"], self.player["HP"] + 5) | |
| # ็ฒๅพ็ถ้ฉๅผ | |
| exp_gain = random.randint(5, 15) | |
| self.player["exp"] += exp_gain | |
| result_text += f"\n\nโจ ็ฒๅพ {exp_gain} ็ถ้ฉๅผ๏ผ" | |
| # ๆชขๆฅๅ็ด | |
| if self.player["exp"] >= 100: | |
| self.level_up() | |
| result_text += f"\n๐ ๆญๅๅ็ด๏ผ็พๅจๆฏ็ญ็ด {self.player['level']}๏ผ" | |
| self.game_history.append(result_text) | |
| # ็ๆๅ ดๆฏๅ็ | |
| scene_image = self.generate_scene_image(scene_description) | |
| return result_text, self.get_status_text(), scene_image | |
| def battle(self, monster): | |
| """ๆฐ้ฌฅ็ณป็ตฑ""" | |
| monster_hp = monster["HP"] | |
| result = "" | |
| while self.player["HP"] > 0 and monster_hp > 0: | |
| # ็ฉๅฎถๆปๆ | |
| player_damage = max(self.player["Attack"] - monster["Defense"], 1) | |
| monster_hp -= player_damage | |
| result += f"ไฝ ๅฐ{monster['name']}้ ๆไบ{player_damage}้ปๅทๅฎณ๏ผ\n" | |
| if monster_hp <= 0: | |
| gold_reward = random.randint(15, 30) | |
| exp_reward = random.randint(20, 40) | |
| self.player["Gold"] += gold_reward | |
| self.player["exp"] += exp_reward | |
| result += f"๐ ไฝ ๆๆไบ{monster['name']}๏ผ\n" | |
| result += f"็ฒๅพ {gold_reward} ้ๅนฃๅ {exp_reward} ็ถ้ฉๅผ๏ผ" | |
| return result | |
| # ๆช็ฉๆปๆ | |
| monster_damage = max(monster["Attack"] - self.player["Defense"], 1) | |
| self.player["HP"] -= monster_damage | |
| result += f"{monster['name']}ๅฐไฝ ้ ๆไบ{monster_damage}้ปๅทๅฎณ๏ผ\n" | |
| if self.player["HP"] <= 0: | |
| result += "๐ ไฝ ่ขซๆๆไบ๏ผ้ๆฒ็ตๆใ" | |
| return result | |
| # ็ฐกๅๆฐ้ฌฅ๏ผ้ฟๅ ็ก้ๅพช็ฐ | |
| if random.random() < 0.3: # 30%ๆฉ็ๅฟซ้็ตๆๆฐ้ฌฅ | |
| if random.choice([True, False]): | |
| result += "ไฝ ๆพๅฐๆฉๆ้่ซไบ๏ผ" | |
| return result | |
| return result | |
| def level_up(self): | |
| """ๅ็ด็ณป็ตฑ""" | |
| self.player["level"] += 1 | |
| self.player["exp"] = 0 | |
| self.player["max_HP"] += 20 | |
| self.player["HP"] = self.player["max_HP"] # ๅ็ดๆๅๆปฟ่ก | |
| self.player["Attack"] += random.randint(2, 5) | |
| self.player["Defense"] += random.randint(1, 3) | |
| def use_potion(self): | |
| """ไฝฟ็จ่ฅๆฐด (ๅ ่็ๆฒป็่ฅๆฐด)""" | |
| if not self.player: | |
| return "่ซๅ ๅตๅปบ่ง่ฒ", "" | |
| potion_name_in_backpack = "ๆฒป็่ฅๆฐด" # ่ๅ ไธญ่ฅๆฐด็ไธญๆๅ็จฑ | |
| if potion_name_in_backpack in self.backpack: | |
| heal_amount = 40 | |
| self.player["HP"] = min(self.player["max_HP"], self.player["HP"] + heal_amount) | |
| self.backpack.remove(potion_name_in_backpack) | |
| result = f"ไฝฟ็จๆฒป็่ฅๆฐด๏ผๆขๅพฉไบ{heal_amount}้ป็ๅฝๅผ๏ผ" | |
| self.game_history.append(result) | |
| return result, self.get_status_text() | |
| else: | |
| return "่ๅ ไธญๆฒๆๆฒป็่ฅๆฐด๏ผ", self.get_status_text() | |
| def shop(self, item_type): | |
| """ๅๅบ็ณป็ตฑ""" | |
| if not self.player: | |
| return "่ซๅ ๅตๅปบ่ง่ฒ", "" | |
| shop_items = { | |
| "healing_potion": {"name": "ๆฒป็่ฅๆฐด", "price": 15, "effect": "ๆขๅพฉ40้ป็ๅฝๅผ"}, | |
| "max_hp_potion": {"name": "็ๅฝ่ฅๆฐด", "price": 30, "effect": "ๆๅคง็ๅฝๅผ+20"}, # ๆฐๅข | |
| "bomb": {"name": "็ธๅฝ", "price": 60, "effect": "ๆปๆๅ+5"}, # ๆฐๅข | |
| "shield": {"name": "็พ็", "price": 50, "effect": "้ฒ็ฆฆๅ+3"} # ๆฐๅข | |
| } | |
| if item_type not in shop_items: | |
| return "็กๆ็็ฉๅ้ธๆ", self.get_status_text() | |
| item = shop_items[item_type] | |
| if self.player["Gold"] >= item["price"]: | |
| self.player["Gold"] -= item["price"] | |
| result = f"ๆๅ่ณผ่ฒท{item['name']}๏ผ่ฑ่ฒป{item['price']}้ๅนฃใ" | |
| if item_type == "healing_potion": | |
| self.backpack.append("ๆฒป็่ฅๆฐด") # ่ๅ ไธญๅญไธญๆๅ็จฑ | |
| elif item_type == "max_hp_potion": | |
| self.player["max_HP"] += 20 | |
| self.player["HP"] = self.player["max_HP"] # ่ณผ่ฒทๆๅๆปฟ่กๅฐๆฐ็ๆๅคงๅผ | |
| result += " ๆๅคง็ๅฝๅผๆๅ๏ผ" | |
| elif item_type == "bomb": | |
| self.backpack.append("็ธๅฝ") | |
| self.player["Attack"] += 5 | |
| result += " ๆปๆๅๆๅ๏ผ" | |
| elif item_type == "shield": | |
| self.backpack.append("็พ็") | |
| self.player["Defense"] += 3 | |
| result += " ้ฒ็ฆฆๅๆๅ๏ผ" | |
| self.game_history.append(result) | |
| return result, self.get_status_text() | |
| else: | |
| return f"้ๅนฃไธ่ถณ๏ผ้่ฆ{item['price']}้ๅนฃใ", self.get_status_text() | |
| def generate_riddle(self): | |
| """ไฝฟ็จAI็ๆไธๅ่ฌ่ชๅๅ ถ็ญๆก""" | |
| if not self.player: | |
| # Return Chinese message for game history/result consistency | |
| return "่ซๅ ๅตๅปบ่ง่ฒ" | |
| try: | |
| prompt = """่ซ็ๆไธๅ้ฉๅๅๅฐไธญๅนด็ดๅญธ็ใๅ ทๆๅฅๅนปๅ้ช้ๆฒ้ขจๆ ผ็็ฐกๅฎ่ฌ่ช๏ผไธฆๆไพๅ ถ็ญๆกใไปฅJSONๆ ผๅผๅๆ๏ผ | |
| {{ | |
| "riddle": "่ฌ่ชๅ งๅฎน", | |
| "answer": "่ฌ่ช็ญๆก" | |
| }} | |
| ่ฌ่ชๆ่ฉฒๆ่ถฃใ็ฐกๅฎๆๆ๏ผ็ญๆกๆฏๅฎ่ฉๆ็ญ่ชใ""" | |
| response = client.models.generate_content( | |
| model="gemini-2.0-flash-001", | |
| contents=prompt | |
| ) | |
| raw_text = response.text.strip() | |
| # ๅฐๆพ JSON ็ฉไปถ็้ๅงๅ็ตๆไฝ็ฝฎ | |
| json_start = raw_text.find('{') | |
| json_end = raw_text.rfind('}') | |
| if json_start != -1 and json_end != -1 and json_end > json_start: | |
| # ๆๅ JSON ๅญไธฒ | |
| json_string = raw_text[json_start : json_end + 1] | |
| riddle_data = json.loads(json_string) | |
| else: | |
| # ๅฆๆๆพไธๅฐๆๆ็ JSON๏ผๆๅบ้ฏ่ชค่ฎๅคๅฑคๆๆ | |
| print(f"๐ซ AI ๅๆๆชๅ ๅซๆๆ็ JSON ็ฉไปถ: {raw_text}") | |
| raise ValueError("AI ๅๆๆชๅ ๅซๆๆ็ JSON ็ฉไปถใ") | |
| self.current_riddle = riddle_data.get("riddle", "AIๆช่ฝ็ๆ่ฌ่ชใ") | |
| self.current_answer = riddle_data.get("answer", "").strip().lower() # ็ญๆก่ฝๅฐๅฏซไปฅไพฟไธๅๅๅคงๅฐๅฏซๆฏ่ผ | |
| result = f"่ฌ่ชๅไบบ็ตฆไฝ ๅบไบไธๅ่ฌ่ช๏ผ\n{self.current_riddle}" | |
| self.game_history.append(result) | |
| return self.current_riddle # ่ฟๅ่ฌ่ชๆๆฌ็ตฆUI้กฏ็คบ | |
| except Exception as e: | |
| print(f"๐ซ AI่ฌ่ช็ๆๅคฑๆ: {str(e)}") | |
| self.current_riddle = "AIๆช่ฝ็ๆ่ฌ่ชใ่ซ็จๅพๅ่ฉฆใ" | |
| self.current_answer = None | |
| result = "่ฌ่ช็ๆๅคฑๆใ" | |
| self.game_history.append(result) | |
| return self.current_riddle # ่ฟๅ้ฏ่ชคๆๆฌ็ตฆUI้กฏ็คบ | |
| def solve_riddle(self, user_answer): | |
| """ๆชขๆฅ่ฌ่ช็ญๆกไธฆ็ตฆไบ็ๅต""" | |
| if not self.player: | |
| return "่ซๅ ๅตๅปบ่ง่ฒ", "", "" # Return Chinese message for game history/result consistency | |
| if self.current_riddle is None or self.current_answer is None: | |
| result = "็ฎๅๆฒๆ่ฌ่ชๅฏไพ่งฃ็ญใ" | |
| self.game_history.append(result) | |
| return result, self.get_status_text(), self.get_history_text() | |
| result = "" | |
| if user_answer.strip().lower() == self.current_answer: | |
| gold_reward = random.randint(30, 70) # ็ๅฐ่ฌ่ช็็ๅต้ๅนฃ | |
| self.player["Gold"] += gold_reward | |
| result = f"โ ๆญๅไฝ ็ญๅฐไบ๏ผ็ญๆกๆฏใ{self.current_answer}ใใไฝ ็ฒๅพไบ {gold_reward} ้ๅนฃ๏ผ" | |
| self.current_riddle = None # ็ญๅฐๅพๆธ ้ค็ถๅ่ฌ่ช | |
| self.current_answer = None | |
| else: | |
| result = f"โ ็ญๆก้ฏ่ชคใๆญฃ็ขบ็ญๆกๆฏใ{self.current_answer}ใใ่ซๅ่ฉฆไธๆฌกๆ็ฒๅๆฐ็่ฌ่ชใ" | |
| # ็ญ้ฏไธๆธ ้ค่ฌ่ช๏ผๅฏไปฅ้่ฉฆ | |
| self.game_history.append(result) | |
| return result, self.get_status_text(), self.get_history_text() | |
| # ๅปบ็ซ้ๆฒ | |
| game = AIAdventureGame() | |
| # ๅฎ็พฉ Gradio ไป้ข (Translate UI elements) | |
| def create_character_ui(name): | |
| """UI handler for character creation""" | |
| status, history = game.create_character(name) | |
| # The game history and status text content is in Chinese, as per original code style. | |
| # Only the UI labels are English. | |
| return status, history, None | |
| def explore_ui(): | |
| """UI handler for exploration""" | |
| result, status, image = game.explore() | |
| history = game.get_history_text() | |
| # result, status, history content is in Chinese. | |
| return result, status, history, image | |
| def use_potion_ui(): | |
| """UI handler for using potion""" | |
| result, status = game.use_potion() | |
| history = game.get_history_text() | |
| # result, status, history content is in Chinese. | |
| return result, status, history | |
| # UI handlers for shop items (update names and calls) | |
| def buy_healing_potion_ui(): | |
| """UI handler for buying healing potion""" | |
| result, status = game.shop("healing_potion") | |
| history = game.get_history_text() | |
| return result, status, history | |
| def buy_bomb_ui(): | |
| """UI handler for buying bomb""" | |
| result, status = game.shop("bomb") | |
| history = game.get_history_text() | |
| return result, status, history | |
| def buy_shield_ui(): | |
| """UI handler for buying shield""" | |
| result, status = game.shop("shield") | |
| history = game.get_history_text() | |
| return result, status, history | |
| def buy_max_hp_potion_ui(): | |
| """UI handler for buying max HP potion""" | |
| result, status = game.shop("max_hp_potion") | |
| history = game.get_history_text() | |
| return result, status, history | |
| # UI handlers for riddle | |
| def get_riddle_ui(): | |
| """UI handler to get a new riddle""" | |
| riddle_text = game.generate_riddle() | |
| status = game.get_status_text() | |
| history = game.get_history_text() | |
| # riddle_text, status, history content is in Chinese. | |
| return riddle_text, "", status, history # Output riddle text, clear answer input, update status/history | |
| def submit_riddle_ui(user_answer): | |
| """UI handler to submit riddle answer""" | |
| result, status, history = game.solve_riddle(user_answer) | |
| # result, status, history content is in Chinese. | |
| return result, "", status, history # Output result, clear answer input, update status/history | |
| # ๅปบ็ซ Gradio ไป้ข (Translate UI elements and add new ones) | |
| with gr.Blocks(title="AI Adventure Game", theme=gr.themes.Soft()) as app: # Translate title | |
| gr.Markdown(""" | |
| # ๐ฎ AI Adventure Game | |
| Welcome to the AI-powered adventure game! Here, every exploration is a unique experience. | |
| **Game Features:** | |
| - ๐ค AI-generated dynamic storylines | |
| - ๐จ AI-created unique monsters | |
| - ๐ผ๏ธ Automatically generated scene images | |
| - โ๏ธ Rich combat and leveling system | |
| **Instructions:** | |
| 1. Enter your character name to create your adventurer. | |
| 2. Click "Start Exploring" to begin your adventure. | |
| 3. Use the shop to buy equipment and supplies. | |
| 4. Use potions during combat to restore health. | |
| """) # Translate Markdown | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| # Character Creation (Translate label and placeholder) | |
| gr.Markdown("## ๐งโโ๏ธ Character Creation") | |
| name_input = gr.Textbox(label="Enter Character Name", placeholder="Enter your adventurer's name") | |
| create_btn = gr.Button("Create Character", variant="primary") # Translate button | |
| # Game Actions (Translate label and buttons) | |
| gr.Markdown("## ๐ฏ Game Actions") | |
| with gr.Row(): | |
| explore_btn = gr.Button("๐บ๏ธ Start Exploring", variant="primary") | |
| potion_btn = gr.Button("๐งช Use Healing Potion", variant="secondary") # Translate button | |
| # Shop (Translate label and buttons, add new items) | |
| gr.Markdown("## ๐ Shop") | |
| with gr.Row(): | |
| buy_healing_potion_btn = gr.Button("Buy Healing Potion (15 Gold)") # Translate button | |
| buy_max_hp_potion_btn = gr.Button("Buy Max HP Potion (30 Gold)") # New button | |
| with gr.Row(): | |
| buy_bomb_btn = gr.Button("Buy Bomb (60 Gold)") # New button | |
| buy_shield_btn = gr.Button("Buy Shield (50 Gold)") # New button | |
| # Riddle Challenge (New section) | |
| gr.Markdown("## ๐ค Riddle Challenge") | |
| riddle_text_output = gr.Textbox(label="Riddle", lines=3, interactive=False, placeholder="Click 'Get Riddle' to receive a challenge.") # New textbox | |
| riddle_answer_input = gr.Textbox(label="Your Answer", placeholder="Enter your answer here.") # New textbox | |
| with gr.Row(): | |
| get_riddle_btn = gr.Button("โ Get Riddle") # New button | |
| submit_riddle_btn = gr.Button("โ Submit Answer", variant="primary") # New button | |
| # Scene Image (Translate label) | |
| scene_image = gr.Image(label="Adventure Scene", height=300) | |
| with gr.Column(scale=1): | |
| # Character Status (Translate label) | |
| status_text = gr.Textbox( | |
| label="๐ Character Status", | |
| lines=12, | |
| interactive=False, | |
| value="Please create a character first." # Translate initial value | |
| ) | |
| # Game Result and History (Translate labels and placeholders) | |
| with gr.Row(): | |
| with gr.Column(): | |
| result_text = gr.Textbox( | |
| label="๐ฎ Game Result", | |
| lines=8, | |
| interactive=False, | |
| placeholder="Game results will appear here..." | |
| ) | |
| with gr.Column(): | |
| history_text = gr.Textbox( | |
| label="๐ Adventure Log", | |
| lines=8, | |
| interactive=False, | |
| placeholder="Your adventure story will be logged here..." | |
| ) | |
| # Bind events (Update button names and add new bindings) | |
| create_btn.click( | |
| create_character_ui, | |
| inputs=[name_input], | |
| outputs=[status_text, history_text, scene_image] | |
| ) | |
| explore_btn.click( | |
| explore_ui, | |
| outputs=[result_text, status_text, history_text, scene_image] | |
| ) | |
| potion_btn.click( | |
| use_potion_ui, | |
| outputs=[result_text, status_text, history_text] | |
| ) | |
| # Bind new shop buttons | |
| buy_healing_potion_btn.click( # Updated button name | |
| buy_healing_potion_ui, | |
| outputs=[result_text, status_text, history_text] | |
| ) | |
| buy_bomb_btn.click( # New binding | |
| buy_bomb_ui, | |
| outputs=[result_text, status_text, history_text] | |
| ) | |
| buy_shield_btn.click( # New binding | |
| buy_shield_ui, | |
| outputs=[result_text, status_text, history_text] | |
| ) | |
| buy_max_hp_potion_btn.click( # New binding | |
| buy_max_hp_potion_ui, | |
| outputs=[result_text, status_text, history_text] | |
| ) | |
| # Bind riddle buttons | |
| get_riddle_btn.click( # New binding | |
| get_riddle_ui, | |
| outputs=[riddle_text_output, riddle_answer_input, status_text, history_text] # Output riddle text, clear answer input, update status/history | |
| ) | |
| submit_riddle_btn.click( # New binding | |
| submit_riddle_ui, | |
| inputs=[riddle_answer_input], | |
| outputs=[result_text, riddle_answer_input, status_text, history_text] # Output result, clear answer input, update status/history | |
| ) | |
| app.launch(show_error=True) |