aicodingfun's picture
Create app.py
b331c31 verified
Raw
History Blame Contribute Delete
34.5 kB
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)