File size: 17,635 Bytes
ef03112
ca6de00
0f8d476
614b4c7
ef03112
0f8d476
 
 
 
 
 
ca6de00
 
0f8d476
 
 
 
 
 
 
 
 
 
 
 
 
ca6de00
 
 
 
 
0f8d476
 
 
 
 
 
 
ca6de00
 
9859dae
ca6de00
 
b8c3d50
 
ef03112
ca6de00
 
b8c3d50
 
ef03112
ca6de00
 
 
b8c3d50
 
ef03112
ca6de00
 
b8c3d50
 
ef03112
ca6de00
 
b8c3d50
 
da69ff5
ca6de00
 
b8c3d50
 
ef03112
 
0f8d476
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
03e2639
 
 
 
 
 
 
 
9859dae
03e2639
 
 
 
 
 
 
ca6de00
 
 
 
 
 
0f8d476
 
 
 
 
 
 
ca6de00
 
 
 
 
da69ff5
ef03112
ca6de00
 
614b4c7
 
 
 
 
 
 
9859dae
bf1efb5
614b4c7
9859dae
da69ff5
ef03112
ca6de00
 
ef03112
9859dae
ef03112
ca6de00
ef03112
ca6de00
ef03112
da69ff5
ef03112
 
da69ff5
 
 
 
 
 
 
ef03112
bf1efb5
ef03112
9859dae
 
bf1efb5
ca6de00
 
da69ff5
 
9859dae
 
da69ff5
03e2639
da69ff5
bf1efb5
9859dae
bf1efb5
9859dae
bf1efb5
 
9859dae
 
 
bf1efb5
 
 
 
da69ff5
9859dae
ca6de00
 
 
da69ff5
ef03112
ca6de00
ef03112
 
 
ca6de00
bf1efb5
2b52dc2
9859dae
2b52dc2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ca6de00
da69ff5
5c254eb
03e2639
 
2b52dc2
9859dae
 
03e2639
 
 
 
 
5c254eb
 
03e2639
5c254eb
 
03e2639
 
5c254eb
 
03e2639
5c254eb
 
03e2639
 
5c254eb
 
03e2639
5c254eb
 
03e2639
614b4c7
9859dae
bf1efb5
9859dae
bf1efb5
03e2639
 
 
 
2b52dc2
 
9859dae
614b4c7
 
 
 
 
 
 
 
 
 
 
 
 
9859dae
03e2639
ca6de00
03e2639
 
 
 
 
 
 
 
ca6de00
ef03112
 
ca6de00
 
ef03112
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
import gradio as gr
from transformers import MarianMTModel, MarianTokenizer
import torch
import random

# ตรวจสอบว่าใช้งาน GPU หรือไม่ (ในกรณีนี้ไม่มี GPU)
device = torch.device("cpu")

# เลือกโมเดลแปลภาษาที่มีขนาดเล็กลง
# คุณสามารถค้นหาโมเดลที่เล็กกว่าได้ที่ Hugging Face Model Hub
# ในตัวอย่างนี้ยังคงใช้ "Helsinki-NLP/opus-mt-th-en" แต่คุณสามารถเปลี่ยนได้ตามต้องการ
model_name = "Helsinki-NLP/opus-mt-th-en"
tokenizer = MarianTokenizer.from_pretrained(model_name)
model = MarianMTModel.from_pretrained(model_name).to(device)
model.eval()  # ตั้งค่าโมเดลเป็นโหมดประเมินผล

# แคชสำหรับการแปลข้อความที่ไม่อยู่ในรายการคงที่
translation_cache = {}

def translate_batch(th_texts):
    """แปลข้อความแบบแบทช์เพื่อเพิ่มความเร็ว"""
    inputs = tokenizer(th_texts, return_tensors="pt", padding=True, truncation=True, max_length=512).to(device)
    with torch.no_grad():
        translated = model.generate(**inputs, max_length=512)
    en_texts = [tokenizer.decode(t, skip_special_tokens=True) for t in translated]
    return en_texts

def translate_th_to_en(th_text: str) -> str:
    th_text = th_text.strip()
    if not th_text:
        return ""
    # ตรวจสอบแคชก่อน
    if th_text in translation_cache:
        return translation_cache[th_text]
    # แปลข้อความ
    en_text = translate_batch([th_text])[0]
    # เก็บในแคช
    translation_cache[th_text] = en_text
    return en_text

# ตัวเลือก (ภาษาไทย) สำหรับ Dropdown
body_shapes_th = [
    "ตัวเหนียวขยุกขยิก", "ตัวปุกปุยกลม", "ตัวเต็มไปด้วยหนาม",
    "ตัวเล็กเหมือนแมลง", "ตัวโปร่งแสงลอยได้", "ตัวโคลนยืดหยุ่น",
    "ตัวหินแข็งแรง", "ตัวทรงพีระมิด", "ตัวระยิบระยับ", "ตัวเหมือนฟองสบู่"
]
heads_th = [
    "หัวตาเดียว", "หัวสองเขาเหมือนมังกร", "หัวมีเขาใหญ่มาก",
    "หัวผีแสยะยิ้ม", "หัวกระต่ายน่ารัก", "หัวปลาหมึกสุดแปลก",
    "หัวเพลิงลุก", "หัวงูพ่นพิษ", "หัววุ้นใส", "หัวเมฆหมอก"
]
arms_legs_th = [
    "แขนหนวดปลาหมึกและขาปลาหมึก", "แขนกลและเท้าสเก็ต",
    "ปีกขนนกและอุ้งเท้าสิงโต", "แขนเรืองแสงและขายานโฮเวอร์",
    "แขนไม้หุ่นเชิดและขายักษ์แมงมุม", "แขนโลหะ",
    "แขนเหล็กข้อต่อ", "ปีกค้างคาว", "ขาไก่ยักษ์", "แขนขนนุ่ม"
]
skin_patterns_th = [
    "ลายสีรุ้ง", "จุดม่วงเรืองแสง", "เกล็ดเหล็ก",
    "ขนนกวิบวับ", "จุดกลมกากเพชร", "ลายหินอ่อน",
    "ลายไฟลุก", "ลายฟ้าแลบ", "ลายสเกลปลา", "ลายหมอก"
]
abilities_th = [
    "พ่นไฟ", "ปล่อยฟองสบู่", "ควบคุมสายฟ้า",
    "ร้องเพลงกล่อม", "วาร์ประยะสั้น", "เสกสิ่งของ",
    "ปล่อยพลังงานแสง", "ควบคุมแรงโน้มถ่วง", "เปลี่ยนรูปร่าง", "เสกภาพลวงตา"
]
moods_th = [
    "เป็นมิตร", "ขี้หงุดหงิด", "ตลกโปกฮา",
    "ซนเป็นลิง", "ขี้อาย", "ขี้เล่น",
    "เศร้าสร้อย", "จริงจัง", "มีความสุข", "ขี้ตกใจ"
]

# แปลล่วงหน้าสำหรับรายการคงที่
# คุณสามารถแปลข้อความเหล่านี้ล่วงหน้านอกโปรแกรมและใส่ค่าลงไปตรงนี้เพื่อความรวดเร็ว
body_shapes_en = {
    "ตัวเหนียวขยุกขยิก": "a squishy and wobbly body",
    "ตัวปุกปุยกลม": "a round and fluffy body",
    "ตัวเต็มไปด้วยหนาม": "a spiky body",
    "ตัวเล็กเหมือนแมลง": "a small, insect-like body",
    "ตัวโปร่งแสงลอยได้": "a translucent, floating body",
    "ตัวโคลนยืดหยุ่น": "a flexible, slime-like body",
    "ตัวหินแข็งแรง": "a strong, rocky body",
    "ตัวทรงพีระมิด": "a pyramidal body",
    "ตัวระยิบระยับ": "a sparkling body",
    "ตัวเหมือนฟองสบู่": "a bubble-like body"
}

heads_en = {
    "หัวตาเดียว": "a single-eyed head",
    "หัวสองเขาเหมือนมังกร": "a two-horned dragon-like head",
    "หัวมีเขาใหญ่มาก": "a head with very large horns",
    "หัวผีแสยะยิ้ม": "a smiling ghostly head",
    "หัวกระต่ายน่ารัก": "a cute rabbit head",
    "หัวปลาหมึกสุดแปลก": "a bizarre squid head",
    "หัวเพลิงลุก": "a flaming head",
    "หัวงูพ่นพิษ": "a venomous snake head",
    "หัววุ้นใส": "a transparent jelly head",
    "หัวเมฆหมอก": "a misty cloud head"
}

arms_legs_en = {
    "แขนหนวดปลาหมึกและขาปลาหมึก": "octopus-like tentacles and legs",
    "แขนกลและเท้าสเก็ต": "mechanical arms and skating feet",
    "ปีกขนนกและอุ้งเท้าสิงโต": "feathered wings and lion-like paws",
    "แขนเรืองแสงและขายานโฮเวอร์": "glowing arms and hover jets",
    "แขนไม้หุ่นเชิดและขายักษ์แมงมุม": "puppet wooden arms and giant spider legs",
    "แขนโลหะ": "metal arms",
    "แขนเหล็กข้อต่อ": "jointed iron arms",
    "ปีกค้างคาว": "bat wings",
    "ขาไก่ยักษ์": "giant chicken legs",
    "แขนขนนุ่ม": "soft feathered arms"
}

skin_patterns_en = {
    "ลายสีรุ้ง": "rainbow patterns",
    "จุดม่วงเรืองแสง": "glowing purple spots",
    "เกล็ดเหล็ก": "iron scales",
    "ขนนกวิบวับ": "shimmering feathers",
    "จุดกลมกากเพชร": "diamond-like round dots",
    "ลายหินอ่อน": "marble patterns",
    "ลายไฟลุก": "flaming patterns",
    "ลายฟ้าแลบ": "lightning patterns",
    "ลายสเกลปลา": "fish scale patterns",
    "ลายหมอก": "misty patterns"
}

abilities_en = {
    "พ่นไฟ": "breathe fire",
    "ปล่อยฟองสบู่": "release soap bubbles",
    "ควบคุมสายฟ้า": "control lightning",
    "ร้องเพลงกล่อม": "sing a lullaby",
    "วาร์ประยะสั้น": "short-range warp",
    "เสกสิ่งของ": "conjure objects",
    "ปล่อยพลังงานแสง": "emit light energy",
    "ควบคุมแรงโน้มถ่วง": "control gravity",
    "เปลี่ยนรูปร่าง": "shape-shift",
    "เสกภาพลวงตา": "create illusions"
}

moods_en = {
    "เป็นมิตร": "friendly",
    "ขี้หงุดหงิด": "irritable",
    "ตลกโปกฮา": "funny",
    "ซนเป็นลิง": "mischievous like a monkey",
    "ขี้อาย": "shy",
    "ขี้เล่น": "playful",
    "เศร้าสร้อย": "sorrowful",
    "จริงจัง": "serious",
    "มีความสุข": "happy",
    "ขี้ตกใจ": "easily startled"
}

def generate_monster_lab(
    body_dd, body_tb,
    head_dd, head_tb,
    arms_dd, arms_tb,
    skin_dd, skin_tb,
    ability_dd, ability_tb,
    mood_dd, mood_tb,
):
    # ถ้า textbox มีข้อความ -> ใช้ค่านั้น, ถ้าว่าง -> ใช้ dropdown
    body_th = body_tb.strip() if body_tb.strip() else body_dd
    head_th = head_tb.strip() if head_tb.strip() else head_dd
    arms_th = arms_tb.strip() if arms_tb.strip() else arms_dd
    skin_th = skin_tb.strip() if skin_tb.strip() else skin_dd
    ability_th = ability_tb.strip() if ability_tb.strip() else ability_dd
    mood_th = mood_tb.strip() if mood_tb.strip() else mood_dd

    desc_th = (
        f"มอนสเตอร์อารมณ์{mood_th} มี{body_th} "
        f"พร้อมด้วย{head_th}, {arms_th}, "
        f"ผิว/ลายแบบ{skin_th}, และพลังพิเศษคือ{ability_th}!"
    )

    # แปลเป็นภาษาอังกฤษ โดยใช้การแปลล่วงหน้าสำหรับรายการคงที่
    body_en = body_shapes_en.get(body_th, translate_th_to_en(body_th))
    head_en = heads_en.get(head_th, translate_th_to_en(head_th))
    arms_en = arms_legs_en.get(arms_th, translate_th_to_en(arms_th))
    skin_en = skin_patterns_en.get(skin_th, translate_th_to_en(skin_th))
    ability_en = abilities_en.get(ability_th, translate_th_to_en(ability_th))
    mood_en = moods_en.get(mood_th, translate_th_to_en(mood_th))

    prompt_en = (
        f"This is a {mood_en} monster with {body_en}, "
        f"a {head_en}, {arms_en}, covered in {skin_en} skin, "
        f"and it can {ability_en}!"
    )

    return desc_th, prompt_en

def random_monster():
    bd = random.choice(body_shapes_th)
    hd = random.choice(heads_th)
    ar = random.choice(arms_legs_th)
    sk = random.choice(skin_patterns_th)
    ab = random.choice(abilities_th)
    md = random.choice(moods_th)
    # Clear textbox
    return (bd, "", hd, "", ar, "", sk, "", ab, "", md, "")

# ---- CSS ----
css_code = """
body {
    background-color: #FFF7EA;
    font-family: "Kanit", sans-serif;
}

#title {
    color: #FF6F91;
    text-align: center;
    font-size: 2rem;
    margin-top: 20px;
    margin-bottom: 10px;
    font-weight: bold;
}
.game-desc {
    margin: 0 auto;
    width: 80%;
    background-color: #FFF2F0;
    border: 2px dashed #FAB1A0;
    border-radius: 10px;
    padding: 15px;
    color: #333;
    margin-bottom: 20px;
}

/* ปุ่มหลัก (สร้างมอนสเตอร์) */
.btn-main {
    background-color: #FFC107;
    border: 2px solid #FFA000;
    font-weight: bold;
    font-size: 1.1rem;
    /* กำหนดความสูงและความยาว (horizontal padding) เยอะๆ */
    padding: 10px 40px;  
    border-radius: 10px;
    margin-right: 10px;
}

/* ปุ่มสุ่ม (รอง) => ตัวเล็กลงในความยาว (horizontal padding) แต่ความสูงเท่ากัน */
.btn-random {
    background-color: #FFE08E; 
    border: 2px solid #FFC107;
    font-weight: normal;
    font-size: 1.1rem;
    /* ใช้ padding vertical เท่ากัน แต่ลด horizontal padding ลง */
    padding: 10px 15px;
    border-radius: 8px;
}
.btn-random:hover {
    background-color: #FFF3C4;
}

#desc-th, #prompt-en {
    background-color: #FFFAE6;
    border: 2px solid #FFE082;
    border-radius: 10px;
    padding: 10px;
    margin-bottom: 20px;
}
"""

def initial_text():
    return "ยังไม่ได้สร้างมอนสเตอร์ ลองเลือกหรือพิมพ์ แล้วกด 'สร้างมอนสเตอร์!' หรือจะกดสุ่มดูก็ได้จ้า"

# ปุ่ม copy
copy_button_html = """
<button style="background-color: #F06292; border: 2px solid #E91E63; font-weight: bold; 
               font-size: 1.1rem; padding: 10px 20px; border-radius: 10px;"
        onclick="copyPromptText()">
  Copy Prompt
</button>
<script>
function copyPromptText() {
  const promptBox = document.querySelector('#prompt-en textarea');
  if (!promptBox) {
    alert('ไม่พบข้อความ Prompt!');
    return;
  }
  const promptText = promptBox.value;
  navigator.clipboard.writeText(promptText);
  alert('คัดลอก Prompt แล้ว!');
}
</script>
"""

with gr.Blocks(css=css_code) as demo:
    gr.Markdown("<h1 id='title'>ZenityX Monster Lab</h1>")
    gr.Markdown("""
    <div class="game-desc">
        <p>หนูน้อยจ๊ะ เลือกได้ว่าจะใช้ค่าใน <strong>Dropdown</strong> หรือจะ <strong>พิมพ์เอง</strong></p>
        <p>หากอยากลุ้นโชค กดปุ่ม (ขวามือ) <strong>"สุ่มมอนสเตอร์สุดเซอร์ไพรส์!"</strong></p>
        <p>เมื่อพร้อมแล้ว กดปุ่ม (ซ้าย) <strong>"สร้างมอนสเตอร์!"</strong> เพื่อดูรายละเอียดและ Prompt</p>
    </div>
    """)

    with gr.Row():
        with gr.Column():
            body_dd = gr.Dropdown(body_shapes_th, label="รูปร่าง", value=body_shapes_th[0])
            body_tb = gr.Textbox(label="หรือพิมพ์เอง", placeholder="ตัวโคลนยืดหยุ่น...")

            head_dd = gr.Dropdown(heads_th, label="หัว/ใบหน้า", value=heads_th[0])
            head_tb = gr.Textbox(label="หรือพิมพ์เอง", placeholder="หัวปลาหมึกสุดแปลก...")

        with gr.Column():
            arms_dd = gr.Dropdown(arms_legs_th, label="แขนขา", value=arms_legs_th[0])
            arms_tb = gr.Textbox(label="หรือพิมพ์เอง", placeholder="แขนออกรากไม้...")

            skin_dd = gr.Dropdown(skin_patterns_th, label="ผิว/ลวดลาย", value=skin_patterns_th[0])
            skin_tb = gr.Textbox(label="หรือพิมพ์เอง", placeholder="ลายทางสีทอง...")

        with gr.Column():
            ability_dd = gr.Dropdown(abilities_th, label="พลังพิเศษ", value=abilities_th[0])
            ability_tb = gr.Textbox(label="หรือพิมพ์เอง", placeholder="สร้างภาพลวงตา...")

            mood_dd = gr.Dropdown(moods_th, label="อารมณ์", value=moods_th[0])
            mood_tb = gr.Textbox(label="หรือพิมพ์เอง", placeholder="ขี้เล่นเป็นพิเศษ...")

    with gr.Row():
        # ปุ่มหลัก (สร้างมอนสเตอร์) -> อยู่ซ้าย
        create_btn = gr.Button("สร้างมอนสเตอร์!", elem_classes="btn-main")
        # ปุ่มรอง (สุ่ม) -> อยู่ขวา, ตัวสั้นลง
        random_btn = gr.Button("สุ่มมอนสเตอร์สุดเซอร์ไพรส์!", elem_classes="btn-random")

    monster_desc_th = gr.Textbox(label="รายละเอียด (ภาษาไทย)", interactive=False, elem_id="desc-th")
    monster_prompt_en = gr.Textbox(label="Prompt (English)", interactive=False, elem_id="prompt-en")

    gr.HTML(copy_button_html)

    # ปุ่มสุ่ม
    random_btn.click(
        fn=random_monster,
        inputs=[],
        outputs=[
            body_dd, body_tb,
            head_dd, head_tb,
            arms_dd, arms_tb,
            skin_dd, skin_tb,
            ability_dd, ability_tb,
            mood_dd, mood_tb
        ]
    )

    # ปุ่มสร้าง
    create_btn.click(
        fn=generate_monster_lab,
        inputs=[
            body_dd, body_tb,
            head_dd, head_tb,
            arms_dd, arms_tb,
            skin_dd, skin_tb,
            ability_dd, ability_tb,
            mood_dd, mood_tb
        ],
        outputs=[monster_desc_th, monster_prompt_en]
    )

    demo.load(fn=initial_text, inputs=None, outputs=monster_desc_th)

demo.launch()