تمرین و پروژه‌های پایتون

ساخت بازی حدس کلمه با پایتون؛ پروژه کامل Hangman با random و Set

ساخت بازی حدس کلمه با پایتون

🗓 آخرین به‌روزرسانی:

ساخت بازی حدس کلمه با پایتون یا Hangman یکی از پروژه‌های کلاسیک و بسیار مفید برای تمرین منطق برنامه‌نویسی است. در این پروژه، برنامه یک کلمه را به‌صورت تصادفی انتخاب می‌کند و کاربر باید با حدس‌زدن حروف، کلمه را قبل از تمام‌شدن فرصت‌ها پیدا کند. در ظاهر ساده است، اما پشت این بازی مفاهیم مهمی مثل رشته‌ها، Set، حلقه while، شرط‌ها، تابع‌ها، اعتبارسنجی ورودی و انتخاب تصادفی قرار دارد.

این مقاله دهمین پروژه فاز اول دسته تمرین و پروژه‌های پایتون است. اگر پروژه‌های قبلی مثل بازی حدس عدد با پایتون و آزمون چهارگزینه‌ای با پایتون را انجام داده باشید، ساختار کلی بازی‌های تعاملی برایتان آشنا خواهد بود.

در این پروژه چه چیزی می‌سازیم؟

  • انتخاب تصادفی کلمه از بانک کلمات
  • نمایش حروف کشف‌شده و مخفی‌کردن بقیه حروف
  • دریافت یک حرف از کاربر
  • جلوگیری از ورود مقدار نامعتبر
  • جلوگیری از حدس تکراری
  • ثبت حروف درست و اشتباه
  • نمایش تعداد فرصت‌های باقی‌مانده
  • نمایش مرحله‌های Hangman به‌صورت ASCII
  • تشخیص برد و باخت
  • امکان خروج از بازی
  • امکان اجرای دوباره بازی

پیش‌نیازهای پروژه

این پروژه به هیچ کتابخانه خارجی نیاز ندارد. فقط از ماژول استاندارد random استفاده می‌کنیم. آشنایی با تابع input در پایتون، حلقه while و Set در پایتون کمک می‌کند منطق کد را راحت‌تر درک کنید.

یک فایل با نام زیر بسازید:

hangman.py

سپس اجرا کنید:

python hangman.py

مرحله اول: ساخت بانک کلمات

برای شروع، یک لیست از کلمات انگلیسی مرتبط با برنامه‌نویسی می‌سازیم:

WORDS = [
    "python",
    "function",
    "variable",
    "string",
    "dictionary",
    "module",
    "exception",
    "object",
    "class",
    "loop",
]

استفاده از لیست باعث می‌شود افزودن یا حذف کلمات ساده باشد. بعداً می‌توانید این بانک را از فایل متنی یا JSON بارگذاری کنید.

مرحله دوم: انتخاب تصادفی کلمه

برای انتخاب یک عضو تصادفی از لیست از random.choice() استفاده می‌کنیم:

import random


def choose_word():
    return random.choice(WORDS)

هر بار که یک دور جدید بازی آغاز می‌شود، یکی از کلمات لیست انتخاب خواهد شد.

مرحله سوم: نگهداری حروف حدس‌زده‌شده با Set

برای ذخیره حروف حدس‌زده‌شده از Set استفاده می‌کنیم:

guessed_letters = set()

دلیل انتخاب Set این است که اعضای تکراری را نگه نمی‌دارد و بررسی وجود یک حرف در آن ساده است. اگر کاربر حرف a را دوباره وارد کند، می‌توانیم سریع تشخیص دهیم که قبلاً حدس زده شده است.

مرحله چهارم: نمایش کلمه مخفی

در ابتدای بازی هیچ حرفی مشخص نیست. برای هر حرفی که هنوز حدس زده نشده، زیرخط نمایش می‌دهیم:

def display_word(
    word,
    guessed_letters,
):
    return " ".join(
        letter
        if letter in guessed_letters
        else "_"
        for letter in word
    )

اگر کلمه python باشد و کاربر حروف p و o را پیدا کرده باشد، خروجی به شکل زیر است:

p _ _ _ o _

مرحله پنجم: ساخت مراحل Hangman

برای هر اشتباه یک مرحله از شکل بازی را نمایش می‌دهیم. تعداد مرحله‌ها تعیین می‌کند کاربر چند اشتباه مجاز دارد.

HANGMAN_STAGES = [
r"""
 +---+
 |   |
     |
     |
     |
     |
=========
""",
r"""
 +---+
 |   |
 O   |
     |
     |
     |
=========
""",
r"""
 +---+
 |   |
 O   |
 |   |
     |
     |
=========
""",
r"""
 +---+
 |   |
 O   |
/|   |
     |
     |
=========
""",
r"""
 +---+
 |   |
 O   |
/|\  |
     |
     |
=========
""",
r"""
 +---+
 |   |
 O   |
/|\  |
/    |
     |
=========
""",
r"""
 +---+
 |   |
 O   |
/|\  |
/ \  |
     |
=========
""",
]

MAX_WRONG = len(HANGMAN_STAGES) - 1

به‌جای نوشتن عدد ۶ به‌صورت ثابت، مقدار فرصت‌ها را از تعداد Stageها محاسبه می‌کنیم. در نتیجه اگر بعداً شکل دیگری اضافه شود، منطق بازی هم خودکار با آن هماهنگ می‌شود.

مرحله ششم: دریافت حرف معتبر

ورودی کاربر باید فقط یک حرف انگلیسی باشد. همچنین نباید اجازه دهیم یک حرف چند بار حدس زده شود.

def get_guess(guessed_letters):
    while True:
        guess = input(
            "یک حرف انگلیسی وارد کنید "
            "(0 برای خروج): "
        ).strip().lower()

        if guess == "0":
            return None

        if (
            len(guess) != 1
            or not ("a" <= guess <= "z")
        ):
            print(
                "فقط یک حرف انگلیسی از a تا z وارد کنید."
            )
            continue

        if guess in guessed_letters:
            print(
                "این حرف را قبلاً حدس زده‌اید."
            )
            continue

        return guess

استفاده از strip() فاصله‌های اضافی را حذف می‌کند و lower() باعث می‌شود حروف بزرگ و کوچک تفاوتی نداشته باشند.

مرحله هفتم: تشخیص برد

برای برد، باید تمام حروف کلمه داخل مجموعه حروف حدس‌زده‌شده باشند.

def has_won(
    word,
    guessed_letters,
):
    return set(word).issubset(
        guessed_letters
    )

این بخش یکی از کاربردهای خوب Set در پروژه واقعی است. set(word) حروف یکتای کلمه را می‌سازد و issubset() بررسی می‌کند همه آن‌ها حدس زده شده‌اند یا نه.

مرحله هشتم: نمایش حروف اشتباه

def wrong_letters(
    word,
    guessed_letters,
):
    return sorted(
        letter
        for letter in guessed_letters
        if letter not in word
    )

نمایش حروف اشتباه به کاربر کمک می‌کند دوباره همان مسیر را تکرار نکند و وضعیت بازی را بهتر ببیند.

ساخت حلقه اصلی یک دور بازی

def play_round():
    word = choose_word()
    guessed_letters = set()
    wrong_count = 0

    while (
        wrong_count < MAX_WRONG
        and not has_won(
            word,
            guessed_letters,
        )
    ):
        print(
            HANGMAN_STAGES[wrong_count]
        )

        print(
            "کلمه:",
            display_word(
                word,
                guessed_letters,
            ),
        )

        mistakes = wrong_letters(
            word,
            guessed_letters,
        )

        if mistakes:
            print(
                "حروف اشتباه:",
                ", ".join(mistakes),
            )

        print(
            "فرصت باقی‌مانده:",
            MAX_WRONG - wrong_count,
        )

        guess = get_guess(
            guessed_letters
        )

        if guess is None:
            print(
                "بازی توسط کاربر متوقف شد."
            )
            return None

        guessed_letters.add(guess)

        if guess in word:
            print("حدس درست بود.")
        else:
            wrong_count += 1
            print(
                "این حرف در کلمه نیست."
            )

تشخیص پایان بازی

پس از خروج از حلقه، یا کاربر کلمه را کامل پیدا کرده یا تعداد اشتباه‌ها به سقف رسیده است.

    print(
        HANGMAN_STAGES[wrong_count]
    )
    print("کلمه:", word)

    if has_won(
        word,
        guessed_letters,
    ):
        print(
            "تبریک! شما برنده شدید."
        )
        return True

    print("فرصت‌ها تمام شد.")
    return False

کد کامل بازی Hangman با پایتون

import random

WORDS = [
    "python",
    "function",
    "variable",
    "string",
    "dictionary",
    "module",
    "exception",
    "object",
    "class",
    "loop",
]

HANGMAN_STAGES = [
r"""
 +---+
 |   |
     |
     |
     |
     |
=========
""",
r"""
 +---+
 |   |
 O   |
     |
     |
     |
=========
""",
r"""
 +---+
 |   |
 O   |
 |   |
     |
     |
=========
""",
r"""
 +---+
 |   |
 O   |
/|   |
     |
     |
=========
""",
r"""
 +---+
 |   |
 O   |
/|\  |
     |
     |
=========
""",
r"""
 +---+
 |   |
 O   |
/|\  |
/    |
     |
=========
""",
r"""
 +---+
 |   |
 O   |
/|\  |
/ \  |
     |
=========
""",
]

MAX_WRONG = len(HANGMAN_STAGES) - 1


def choose_word():
    return random.choice(WORDS)


def display_word(
    word,
    guessed_letters,
):
    return " ".join(
        letter
        if letter in guessed_letters
        else "_"
        for letter in word
    )


def wrong_letters(
    word,
    guessed_letters,
):
    return sorted(
        letter
        for letter in guessed_letters
        if letter not in word
    )


def get_guess(guessed_letters):
    while True:
        guess = input(
            "یک حرف انگلیسی وارد کنید "
            "(0 برای خروج): "
        ).strip().lower()

        if guess == "0":
            return None

        if (
            len(guess) != 1
            or not ("a" <= guess <= "z")
        ):
            print(
                "فقط یک حرف انگلیسی "
                "از a تا z وارد کنید."
            )
            continue

        if guess in guessed_letters:
            print(
                "این حرف را قبلاً حدس زده‌اید."
            )
            continue

        return guess


def has_won(
    word,
    guessed_letters,
):
    return set(word).issubset(
        guessed_letters
    )


def play_round():
    word = choose_word()
    guessed_letters = set()
    wrong_count = 0

    while (
        wrong_count < MAX_WRONG
        and not has_won(
            word,
            guessed_letters,
        )
    ):
        print(
            HANGMAN_STAGES[wrong_count]
        )

        print(
            "کلمه:",
            display_word(
                word,
                guessed_letters,
            ),
        )

        mistakes = wrong_letters(
            word,
            guessed_letters,
        )

        if mistakes:
            print(
                "حروف اشتباه:",
                ", ".join(mistakes),
            )

        print(
            "فرصت باقی‌مانده:",
            MAX_WRONG - wrong_count,
        )

        guess = get_guess(
            guessed_letters
        )

        if guess is None:
            print(
                "بازی توسط کاربر متوقف شد."
            )
            return None

        guessed_letters.add(guess)

        if guess in word:
            print("حدس درست بود.")
        else:
            wrong_count += 1
            print(
                "این حرف در کلمه نیست."
            )

    print(
        HANGMAN_STAGES[wrong_count]
    )
    print("کلمه:", word)

    if has_won(
        word,
        guessed_letters,
    ):
        print(
            "تبریک! شما برنده شدید."
        )
        return True

    print("فرصت‌ها تمام شد.")
    return False


def main():
    print(
        "=== بازی حدس کلمه Hangman "
        "با پایتون ==="
    )

    while True:
        result = play_round()

        if result is None:
            break

        again = input(
            "\nدوباره بازی کنیم؟ "
            "(بله/خیر): "
        ).strip().lower()

        if again not in {
            "بله",
            "yes",
            "y",
        }:
            print("بازی پایان یافت.")
            break


if __name__ == "__main__":
    main()

نمونه اجرای بازی

=== بازی حدس کلمه Hangman با پایتون ===
کلمه: _ _ _ _ _ _
فرصت باقی‌مانده: 6
یک حرف انگلیسی وارد کنید: p
حدس درست بود.

کلمه: p _ _ _ _ _

چرا از Set استفاده کردیم؟

اگر حروف حدس‌زده‌شده را در یک List نگه داریم، بازی همچنان کار می‌کند، اما Set برای این مسئله مناسب‌تر است. چون تکرار ندارد و هدف ما فقط دانستن این است که یک حرف قبلاً وارد شده یا نه. همین پروژه نمونه خوبی برای درک تفاوت List و Set است.

اشتباه‌های رایج در ساخت Hangman

کم‌کردن فرصت برای حدس تکراری

اگر کاربر حرفی را قبلاً وارد کرده باشد، نباید دوباره جریمه شود. ابتدا باید تکراری‌بودن ورودی بررسی شود و بعد سراغ درست یا غلط بودن آن برویم.

استفاده از یک رشته برای نگهداری تمام حدس‌ها

استفاده از رشته ممکن است ممکن باشد، اما Set برای بررسی عضویت و جلوگیری از تکرار خواناتر است.

مقایسه حروف بزرگ و کوچک

بدون lower()، حروف P و p دو مقدار متفاوت خواهند بود. بهتر است ورودی را قبل از پردازش Normalize کنیم.

نوشتن تمام منطق داخل main

تفکیک کد به توابع choose_word، display_word، get_guess و play_round باعث می‌شود پروژه قابل تست و توسعه باشد.

چطور پروژه را حرفه‌ای‌تر کنیم؟

  1. سطح سختی: برای Easy، Medium و Hard تعداد فرصت‌ها و طول کلمات را تغییر دهید.
  2. دسته‌بندی کلمات: کلمات Python، Web، Database و General را جدا کنید.
  3. خواندن کلمات از فایل: بانک کلمات را از فایل متنی بارگذاری کنید.
  4. ذخیره امتیاز: تعداد برد و باخت را داخل JSON ثبت کنید.
  5. راهنما یا Hint: برای هر کلمه یک توضیح کوتاه تعریف کنید.
  6. حدس کل کلمه: علاوه بر حرف، امکان واردکردن پاسخ کامل را اضافه کنید.
  7. نسخه فارسی: بانک کلمات فارسی بسازید و اعتبارسنجی حروف را متناسب با فارسی تغییر دهید.
  8. نسخه GUI: با Tkinter بازی را از ترمینال به رابط گرافیکی منتقل کنید.

تمرین پیشنهادی

بانک کلمات را از کد خارج کنید و داخل فایل words.txt قرار دهید. سپس هنگام اجرای برنامه، فایل را بخوانید، خط‌های خالی را حذف کنید و یکی از کلمات را با random.choice() انتخاب کنید. این تمرین ترکیب خوبی از پروژه Hangman با مباحث فایل در پایتون است.

بعد از Hangman می‌توانید یک بازی گرافیکی بسازید؛ آموزش بازی مار با پایتون و Pygame قدم‌به‌قدم همین کار را انجام می‌دهد. فهرست کامل پروژه‌ها به‌ترتیب سطح در ۳۲ پروژه پایتون از مبتدی تا پیشرفته آمده است.

این پروژه چه مفاهیمی را تمرین می‌دهد؟

  • ماژول random
  • random.choice
  • رشته‌ها و پیمایش حروف
  • List و Set
  • Set Membership
  • حلقه while
  • شرط‌های چندمرحله‌ای
  • تابع‌ها و مقدار بازگشتی
  • input و اعتبارسنجی ورودی
  • List Comprehension و Generator Expression
  • ASCII Art
  • مدیریت State در یک بازی

جمع‌بندی

در این آموزش یک بازی کامل حدس کلمه یا Hangman با پایتون ساختیم. برنامه از یک بانک کلمات، کلمه‌ای تصادفی انتخاب می‌کند، حروف واردشده را داخل Set نگه می‌دارد، حدس‌های تکراری را رد می‌کند، کلمه مخفی را مرحله‌به‌مرحله نمایش می‌دهد و با توجه به تعداد اشتباه‌ها برد یا باخت را تشخیص می‌دهد.

مهم‌ترین ارزش این پروژه در ترکیب چند مفهوم پایه در یک منطق واقعی است. بعد از اجرای نسخه اصلی، پیشنهاد می‌شود قابلیت سطح سختی یا بارگذاری کلمات از فایل را اضافه کنید تا پروژه از یک تمرین ساده به یک بازی قابل توسعه تبدیل شود.

📌 همچنین بخوانید: تمرین پایتون با جواب، ساخت بازی با پایتون و Pygame و ساخت ربات با پایتون

می‌خواهی پیشرفتت ذخیره شود؟با ورود یا ثبت‌نام رایگان، درس‌های تمام‌شده را علامت بزن و درصد پیشرفتت را در حساب کاربری ببین.ورود / ثبت‌نام برای ذخیره پیشرفت
درس بعدی · پروژه‌های پایتون (11 از 19)ساخت بازی دوز با پایتون؛ پروژه کامل Tic-Tac-Toe دو نفره با تشخیص برد ←
آموزش‌های تازه، کد آماده و رفع خطاهر هفته در کانال Dev Source؛ رایگان.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *