import math import gradio as gr from transformers import MarianMTModel, MarianTokenizer import torch ############################################## # 1) โหลดโมเดล MarianMT สำหรับแปลไทย->อังกฤษ ############################################## model_name = "Helsinki-NLP/opus-mt-th-en" tokenizer = MarianTokenizer.from_pretrained(model_name) model = MarianMTModel.from_pretrained(model_name) def translate_th_to_en(text_th: str) -> str: text_th = text_th.strip() if not text_th: return "" inputs = tokenizer(text_th, return_tensors="pt", max_length=512, truncation=True) translation_tokens = model.generate(**inputs, max_length=512) en_text = tokenizer.decode(translation_tokens[0], skip_special_tokens=True) return en_text ############################################## # 2) สูตรคำนวณ Black Body (Stefan-Boltzmann) + Greenhouse เบื้องต้น ############################################## def approximate_temp_with_star(star_type, distance_au, albedo=0.3): """ คำนวณอุณหภูมิพื้นผิว (°C) อย่างง่าย โดยอิง Black Body + star luminosity - star_type: "Red Dwarf" -> luminosity ~0.02 เท่าดวงอาทิตย์ "Sun-like" -> luminosity = 1 เท่าดวงอาทิตย์ "Blue Giant"-> luminosity ~10 เท่าดวงอาทิตย์ (สมมุติ) - distance_au: ระยะห่างหน่วย AU - albedo: สัดส่วนสะท้อนแสง (สมมุติ 0.3) - สุดท้ายบวก greenhouse effect ~ +15 °C (สมมุติ) """ if star_type == "Red Dwarf": lum_ratio = 0.02 # สมมุติ elif star_type == "Blue Giant": lum_ratio = 10.0 else: # Sun-like lum_ratio = 1.0 # ความสว่างของดวงอาทิตย์ ~3.828e26 W luminosity = 3.828e26 * lum_ratio # แปลง AU -> เมตร dist_m = distance_au * 1.496e11 sigma = 5.67e-8 # Stefan-Boltzmann constant # T(K) = [ ( (1 - A) * L ) / (16*pi*sigma * d^2 ) ]^(1/4) T_k = ((1 - albedo) * luminosity / (16 * math.pi * sigma * dist_m**2))**0.25 T_c = T_k - 273.15 # บวก greenhouse เล็กน้อย T_c += 15 return round(T_c) def approximate_gravity(diameter_factor): """ สมมุติว่า แรงโน้มถ่วง ~ diameter_factor เท่าของโลก (จริงๆ เกี่ยวกับความหนาแน่นด้วย แต่เอาแบบง่าย) """ return round(diameter_factor, 2) ############################################## # 3) ฟังก์ชันแปลงตัวเลข -> คำบรรยายเชิงเปรียบเทียบ ############################################## def describe_distance(distance_au): """ ตีความระยะห่าง (AU) เป็นคำบรรยาย """ if distance_au < 0.5: return "โคจรอยู่ใกล้มากจนดาวเคราะห์ร้อนระอุ" elif distance_au < 1.2: return "โคจรค่อนข้างใกล้เหมือนโลก หรืออาจอุ่นกว่านิดหน่อย" elif distance_au < 2.5: return "โคจรห่างพอประมาณ ค่อนข้างเย็น" else: return "โคจรไกลมาก มีสภาพหนาวเย็นสุดขั้ว" def describe_temp(temp_c): """ ตีความอุณหภูมิ (°C) เป็นคำบรรยาย """ if temp_c < -30: return "อากาศหนาวแข็ง" elif temp_c < 10: return "อากาศค่อนข้างเย็น" elif temp_c < 35: return "อากาศกำลังสบาย" else: return "ร้อนระอุ" def describe_gravity(g): """ ตีความแรงโน้มถ่วง (g) เป็นคำบรรยาย """ if g < 0.5: return "โน้มถ่วงเบามาก (ตัวลอยได้ง่าย)" elif g < 1.2: return "คล้ายโลก" elif g < 2.0: return "ค่อนข้างหนัก" else: return "หนักมากจนอาจกดทับทุกสิ่ง" def describe_tilt(tilt_deg): """ ตีความแกนเอียง """ tilt = float(tilt_deg) if tilt < 5: return "แทบไม่เอียง ฤดูกาลเรียบง่าย" elif tilt < 25: return "เอียงเล็กน้อย มีฤดูกาลพอประมาณ" elif tilt < 45: return "เอียงปานกลาง ฤดูกาลค่อนข้างเปลี่ยนแปลง" else: return "เอียงมาก ฤดูกาลสุดขั้ว" def describe_moons(n): """ ตีความจำนวนดวงจันทร์ """ n = int(n) if n == 0: return "ไม่มีดวงจันทร์" elif n == 1: return "มีดวงจันทร์หนึ่งดวง" else: return f"มีดวงจันทร์ {n} ดวง" ############################################## # 4) สร้าง prompt 3 แบบ (ไม่มีตัวเลขตรงๆ) ############################################## def build_prompts_en(planet_name_en, star_type_en, distance_desc_en, temp_desc_en, gravity_desc_en, tilt_desc_en, moon_desc_en, oxygen_desc_en, life_en): """ สร้าง 3 prompts ภาษาอังกฤษ โดยไม่ใส่ตัวเลขตรงๆ แต่ใช้คำบรรยายเชิงเปรียบเทียบ """ # Prompt 1: ภาพดาวจากอวกาศ prompt1 = ( f"A stunning space illustration of planet '{planet_name_en}' orbiting a {star_type_en} star. " f"It is {distance_desc_en}, with {temp_desc_en} climate and {gravity_desc_en} surface gravity. " f"{tilt_desc_en}, and {moon_desc_en}. Atmosphere containing {oxygen_desc_en}, truly fascinating." ) # Prompt 2: สิ่งมีชีวิต prompt2 = ( f"Alien life on planet '{planet_name_en}': {life_en}, " f"thriving under {temp_desc_en} conditions, {gravity_desc_en} gravity, and {oxygen_desc_en} in the air. " f"Highly imaginative, concept art style, fantasy world." ) # Prompt 3: พื้นผิวดาว prompt3 = ( f"The planet '{planet_name_en}' has a surface with {temp_desc_en} weather, {gravity_desc_en}, " f"{tilt_desc_en}, and {moon_desc_en}. An epic terrain, atmospheric glow, and {oxygen_desc_en} presence. " f"Realistic space painting, cinematic lighting." ) prompt_all = ( "--- Prompt #1 ---\n" + prompt1 + "\n\n" "--- Prompt #2 ---\n" + prompt2 + "\n\n" "--- Prompt #3 ---\n" + prompt3 + "\n" ) return prompt_all ############################################## # 5) ฟังก์ชันหลัก generate_planet_info ############################################## def generate_planet_info( planet_name_th, star_type_en_select, # ("Red Dwarf", "Sun-like", "Blue Giant") distance_str, diameter_str, tilt_str, moon_str, oxygen_percent, planet_type_th, # ("ดาวหิน", "ดาวก๊าซ", "ดาวน้ำแข็ง") life_th ): # แปลง string -> float/int try: distance_au = float(distance_str) except: distance_au = 1.0 try: diameter_factor = float(diameter_str) except: diameter_factor = 1.0 try: tilt_deg = float(tilt_str) except: tilt_deg = 23.5 try: num_moon = int(moon_str) except: num_moon = 1 # 1) คำนวณอุณหภูมิ (°C) temp_c = approximate_temp_with_star(star_type_en_select, distance_au) # 2) คำนวณแรงโน้มถ่วง g_approx = approximate_gravity(diameter_factor) # 3) สร้าง “สรุปสำหรับเด็ก” (ภาษาไทย) # โดยยังไม่ได้รวม star_type_en_select เพราะเป็นภาษาอังกฤษ # แต่เราจะใช้ planet_type_th ด้วย # สมมุติ child_friendly_summary: # => โชว์ temp, g, oxygen, life child_summary = ( f"ดาว {planet_name_th} เป็น{planet_type_th} " f"โคจรรอบดาวฤกษ์ประเภท {star_type_en_select} (อิงวิทยาศาสตร์สมมุติ)\n" f"อุณหภูมิประมาณ {temp_c}°C, " f"แรงโน้มถ่วง ~{g_approx}g, ออกซิเจน ~{oxygen_percent}%, " f"แกนเอียง {tilt_deg}°, {num_moon} ดวงจันทร์,\n" f"มีสิ่งมีชีวิต: {life_th}\n" f"น่าอัศจรรย์จริง ๆ!" ) # 4) รายละเอียดเชิงเทคนิค (ไทย) detail_th = ( f"ชื่อดาวเคราะห์ (ไทย): {planet_name_th}\n" f"ประเภทดาวฤกษ์: {star_type_en_select}\n" f"ระยะห่าง: ~{distance_au} AU\n" f"ขนาดเส้นผ่านศูนย์กลาง: ~{diameter_factor} เท่าโลก\n" f"แกนเอียง: ~{tilt_deg}°\n" f"จำนวนดวงจันทร์: {num_moon}\n" f"อุณหภูมิคำนวณ: ~{temp_c} °C\n" f"แรงโน้มถ่วง: ~{g_approx} g\n" f"ออกซิเจน: {oxygen_percent}%\n" f"ชนิดดาวเคราะห์ (ไทย): {planet_type_th}\n" f"สิ่งมีชีวิต (ไทย): {life_th}\n" ) # 5) แปล planet_name_th -> English planet_name_en = translate_th_to_en(planet_name_th) # แปล life_th -> อังกฤษ life_en = translate_th_to_en(life_th) # แมป planet_type_th -> Eng # (ดาวหิน -> rocky planet, ดาวก๊าซ -> gas giant, ดาวน้ำแข็ง -> icy planet) type_map = { "ดาวหิน": "rocky planet", "ดาวก๊าซ": "gas giant", "ดาวน้ำแข็ง": "icy planet" } # (กรณี user พิมพ์ว่า "ดาวหิน" หรือมีวงเล็บ อาจต้อง .replace ให้เคลียร์) planet_type_key = planet_type_th.replace("(", "").replace(")", "").replace(" ", "").split("ดาว")[-1] planet_type_key = planet_type_key.strip() planet_type_en = type_map.get(planet_type_key, "unknown planet") # 6) ตีความ distance, temp, gravity, tilt, moon, oxygen เพื่ออธิบายเป็นอังกฤษ (non-numeric) # - distance distance_desc_th = describe_distance(distance_au) distance_desc_en = translate_th_to_en(distance_desc_th) # - temp temp_desc_th = describe_temp(temp_c) temp_desc_en = translate_th_to_en(temp_desc_th) # - gravity gravity_desc_th = describe_gravity(g_approx) gravity_desc_en = translate_th_to_en(gravity_desc_th) # - tilt tilt_desc_th = describe_tilt(tilt_deg) tilt_desc_en = translate_th_to_en(tilt_desc_th) # - moon moon_desc_th = describe_moons(num_moon) moon_desc_en = translate_th_to_en(moon_desc_th) # - oxygen # ถ้า oxygen 0 -> no oxygen # oxygen < 5 -> extremely low oxygen # etc. if oxygen_percent < 1: oxygen_desc_th = "ไม่มีออกซิเจน" elif oxygen_percent < 10: oxygen_desc_th = "ออกซิเจนจางมาก" elif oxygen_percent < 25: oxygen_desc_th = "ออกซิเจนพอเหมาะ" else: oxygen_desc_th = "ออกซิเจนสูง" oxygen_desc_en = translate_th_to_en(oxygen_desc_th) # 7) สร้าง 3 Prompts (English) prompt_all = build_prompts_en( planet_name_en, f"{star_type_en_select} star", # e.g. "Red Dwarf star" distance_desc_en, temp_desc_en, gravity_desc_en, tilt_desc_en, moon_desc_en, oxygen_desc_en, life_en ) return child_summary, detail_th, prompt_all ############################################## # 6) สร้าง UI (Gradio) ############################################## css_code = """ body { background-color: #F9FBFF; font-family: "Kanit", sans-serif; } #title { color: #4A90E2; text-align: center; font-size: 2rem; margin-top: 20px; margin-bottom: 10px; font-weight: bold; } .game-desc { margin: 0 auto; width: 90%; background-color: #ECF6FF; border: 2px dashed #B3DAFF; border-radius: 10px; padding: 15px; color: #333; margin-bottom: 20px; } .btn-main { background-color: #FFE066; border: 2px solid #FFCA28; font-weight: bold; font-size: 1.1rem; padding: 10px 30px; border-radius: 10px; margin-right: 10px; } #child-summary, #detail-th, #prompt-en { background-color: #FFFDF5; border: 2px solid #FFE082; border-radius: 10px; padding: 10px; margin-bottom: 20px; } """ def welcome_text(): return "ยินดีต้อนรับสู่ Planetary Adventure+! ลองกรอกข้อมูลแล้วกด 'สร้างโลกแฟนตาซี' กันนะจ๊ะ" copy_button_html = """ """ import re with gr.Blocks(css=css_code) as demo: gr.Markdown("

Planetary Adventure (Advanced) - Thai Input + Real Sci + No Direct Numbers

") gr.Markdown("""

สร้างดาวเคราะห์พร้อมหลักวิทยาศาสตร์เบื้องต้น (สูตร Stefan-Boltzmann) และใช้การแปล MarianMT

  1. กรอกชื่อดาวเคราะห์ (ภาษาไทย)
  2. เลือกชนิดดาวฤกษ์ (Red Dwarf / Sun-like / Blue Giant)
  3. กรอกระยะห่าง (หน่วย AU), ขนาดเท่าโลก, แกนเอียง, จำนวนดวงจันทร์, % ออกซิเจน
  4. ระบุประเภทดาวเคราะห์ (ไทย) เช่น "ดาวหิน", "ดาวก๊าซ", "ดาวน้ำแข็ง"
  5. พิมพ์สิ่งมีชีวิต (ไทย)
  6. กดปุ่ม "สร้างโลกแฟนตาซี"

ระบบจะ คำนวณอุณหภูมิ, แรงโน้มถ่วง, ตีความเป็นคำบรรยาย, และแปลไทย->อังกฤษ
จากนั้นสร้าง 3 prompts ที่เลี่ยงตัวเลขตรงๆ ใช้คำเปรียบเทียบแทน

""") planet_name_th = gr.Textbox(label="ชื่อดาวเคราะห์ (ภาษาไทย)", placeholder="ตัวอย่าง: ดาวซานาดา") star_type_select = gr.Dropdown( label="ประเภทดาวฤกษ์ (English)", choices=["Red Dwarf", "Sun-like", "Blue Giant"], value="Sun-like" ) distance_au = gr.Textbox(label="ระยะห่างจากดาวฤกษ์ (AU)", placeholder="เช่น 1, 0.5, 2") diameter_factor = gr.Textbox(label="ขนาด (เท่าโลก)", placeholder="เช่น 1, 2, 0.5") tilt_deg = gr.Textbox(label="แกนเอียง (องศา)", placeholder="0-90") moon_number = gr.Textbox(label="จำนวนดวงจันทร์", placeholder="0, 1, 2, ...") oxygen_slider = gr.Slider( minimum=0, maximum=100, step=1, value=21, label="% ออกซิเจน (ในบรรยากาศ)" ) planet_type_th = gr.Dropdown( label="ชนิดดาวเคราะห์ (ไทย)", choices=["ดาวหิน", "ดาวก๊าซ", "ดาวน้ำแข็ง"], value="ดาวหิน" ) life_th = gr.Textbox(label="สิ่งมีชีวิต (ภาษาไทย)", placeholder="ตัวอย่าง: แมลงยักษ์เรืองแสง") create_btn = gr.Button("สร้างโลกแฟนตาซี", elem_classes="btn-main") child_summary_out = gr.Textbox(label="สรุปสำหรับเด็ก (ไทย)", interactive=False, elem_id="child-summary") detail_th_out = gr.Textbox(label="รายละเอียด (ไทย)", interactive=False, elem_id="detail-th") prompt_en_out = gr.Textbox(label="3 Prompts (English)", interactive=False, elem_id="prompt-en") gr.HTML(copy_button_html) create_btn.click( fn=generate_planet_info, inputs=[ planet_name_th, # text star_type_select, # red dwarf / sun-like / blue giant distance_au, diameter_factor, tilt_deg, moon_number, oxygen_slider, planet_type_th, # "ดาวหิน", "ดาวก๊าซ", "ดาวน้ำแข็ง" life_th ], outputs=[ child_summary_out, detail_th_out, prompt_en_out ] ) demo.load(fn=welcome_text, inputs=None, outputs=child_summary_out) demo.launch()