ساخت بازی سنگ کاغذ قیچی با پایتون؛ پروژه کامل با امتیازدهی

🗓 آخرین بهروزرسانی:
ساخت بازی سنگ، کاغذ، قیچی با پایتون یکی از تمرینهای ساده اما بسیار مفید برای تبدیل مفاهیم پایه برنامهنویسی به یک پروژه واقعی است. در این پروژه با دریافت ورودی از کاربر، انتخاب تصادفی کامپیوتر، مقایسه حالتها، مدیریت امتیاز، حلقه بازی و اعتبارسنجی ورودی کار میکنیم. نتیجه فقط یک مثال چندخطی نیست؛ در پایان یک بازی کامل داریم که چند دور اجرا میشود، امتیاز بازیکن و کامپیوتر را نگه میدارد و اولین طرفی که به امتیاز هدف برسد برنده مسابقه میشود.
اگر پروژه قبلی یعنی ساخت بازی حدس عدد با پایتون را انجام داده باشید، بخشی از منطق این پروژه برایتان آشنا خواهد بود؛ اما این بار بهجای مقایسه عددها باید قوانین یک بازی سهحالته را به کد تبدیل کنیم. همچنین برای دریافت ورودی از تابع input در پایتون، برای تصمیمگیری از شرطهای if، elif و else و برای اجرای چندباره بازی از حلقه while استفاده میکنیم.
در این پروژه چه چیزی میسازیم؟
بازی ما بهصورت متنی در ترمینال اجرا میشود. کاربر یکی از سه گزینه سنگ، کاغذ یا قیچی را وارد میکند و کامپیوتر نیز بهصورت تصادفی یکی از همین گزینهها را انتخاب میکند. سپس برنامه برنده هر دور را مشخص میکند و امتیازها را بهروزرسانی میکند.
- دریافت انتخاب کاربر
- انتخاب تصادفی کامپیوتر
- تشخیص مساوی
- تشخیص برنده هر دور
- نمایش انتخابها با ایموجی
- ثبت امتیاز بازیکن و کامپیوتر
- ادامه بازی تا رسیدن یکی از طرفین به ۳ امتیاز
- مدیریت ورودی اشتباه
- امکان خروج وسط مسابقه
- امکان شروع مسابقه جدید بعد از پایان
قوانین بازی سنگ، کاغذ، قیچی
قبل از کدنویسی باید منطق بازی را دقیق بنویسیم. این مرحله مهم است، چون برنامهنویسی یعنی تبدیل یک قانون یا مسئله به مجموعهای از تصمیمهای قابل اجرا.
- سنگ، قیچی را میبرد.
- قیچی، کاغذ را میبرد.
- کاغذ، سنگ را میبرد.
- اگر انتخاب هر دو طرف یکسان باشد، دور مساوی است.
برای مثال اگر بازیکن «سنگ» انتخاب کند و کامپیوتر «قیچی»، بازیکن برنده است. اگر بازیکن «سنگ» و کامپیوتر «کاغذ» انتخاب کنند، کامپیوتر برنده میشود.
پیشنیازهای پروژه
برای اجرای این پروژه فقط Python نیاز دارید. هیچ کتابخانه خارجی نصب نمیشود، چون ماژول random که برای انتخاب تصادفی استفاده میکنیم بخشی از کتابخانه استاندارد پایتون است.
یک فایل جدید با نام زیر بسازید:
rock_paper_scissors.pyسپس فایل را با دستور زیر اجرا کنید:
python rock_paper_scissors.pyمرحله اول: تعریف گزینههای بازی
بهتر است گزینههای مجاز را یک بار تعریف کنیم و در قسمتهای مختلف برنامه از همان مجموعه استفاده کنیم. در این پروژه از Tuple استفاده میکنیم:
CHOICES = ("سنگ", "کاغذ", "قیچی")این کار دو مزیت دارد. اول اینکه احتمال اشتباه تایپی کمتر میشود و دوم اینکه اگر بعداً بخواهیم گزینه جدیدی به بازی اضافه کنیم، محل اصلی تنظیمات مشخص است.
برای زیباتر شدن خروجی نیز یک دیکشنری ساده برای ایموجیها میسازیم:
EMOJIS = {
"سنگ": "🪨",
"کاغذ": "📄",
"قیچی": "✂️"
}مرحله دوم: انتخاب تصادفی کامپیوتر
کامپیوتر نباید همیشه انتخاب ثابت داشته باشد. با random.choice() میتوانیم یکی از گزینههای موجود را بهصورت تصادفی انتخاب کنیم.
import random
computer_choice = random.choice(CHOICES)در هر دور بازی، مقدار computer_choice میتواند سنگ، کاغذ یا قیچی باشد.
مرحله سوم: دریافت انتخاب کاربر
در سادهترین حالت میتوانیم بنویسیم:
user_choice = input("سنگ، کاغذ یا قیچی؟ ")اما این نسخه یک مشکل دارد. اگر کاربر کلمهای غیر از سه انتخاب مجاز وارد کند، برنامه همچنان ادامه پیدا میکند. بهتر است یک تابع مخصوص دریافت ورودی بسازیم تا فقط مقدار معتبر را برگرداند.
def get_user_choice():
while True:
choice = input(
"انتخاب شما (سنگ/کاغذ/قیچی یا خروج): "
).strip()
if choice == "خروج":
return None
if choice in CHOICES:
return choice
print(
"ورودی نامعتبر است. فقط سنگ، کاغذ، قیچی یا خروج را وارد کنید."
)تا زمانی که ورودی معتبر نباشد، حلقه ادامه پیدا میکند. اگر کاربر «خروج» را وارد کند، مقدار None برگردانده میشود و بعداً از همین مقدار برای پایان دادن به مسابقه استفاده خواهیم کرد.
مرحله چهارم: طراحی تابع تشخیص برنده
میتوانیم برای هر حالت چندین شرط تو در تو بنویسیم، اما ساختار تمیزتری هم وجود دارد. سه ترکیب برنده بازیکن را داخل یک مجموعه قرار میدهیم:
winning_pairs = {
("سنگ", "قیچی"),
("کاغذ", "سنگ"),
("قیچی", "کاغذ"),
}هر Tuple شامل انتخاب بازیکن و انتخاب کامپیوتر است. حالا تابع تشخیص برنده بسیار خواناتر میشود:
def determine_winner(user, computer):
if user == computer:
return "draw"
winning_pairs = {
("سنگ", "قیچی"),
("کاغذ", "سنگ"),
("قیچی", "کاغذ"),
}
if (user, computer) in winning_pairs:
return "user"
return "computer"ابتدا مساوی بودن بررسی میشود. اگر انتخابها برابر نباشند، برنامه بررسی میکند که ترکیب فعلی داخل مجموعه حالتهای برنده بازیکن وجود دارد یا نه. اگر وجود داشته باشد، کاربر برنده است؛ در غیر این صورت کامپیوتر برنده خواهد بود.
چرا این روش بهتر از شرطهای طولانی است؟
میشد تمام حالتها را با if و or نوشت، اما کد خیلی سریع طولانی و سختخوان میشد. وقتی قوانین یک مسئله را بهصورت داده ذخیره میکنیم، منطق اصلی برنامه کوتاهتر و قابل نگهداریتر میشود. این یکی از مهارتهایی است که در پروژههای واقعی اهمیت زیادی دارد.
مرحله پنجم: ساخت سیستم امتیازدهی
در نسخه ما مسابقه تا زمانی ادامه پیدا میکند که بازیکن یا کامپیوتر به ۳ امتیاز برسد. بنابراین دو متغیر برای امتیازها نیاز داریم:
user_score = 0
computer_score = 0شرط ادامه مسابقه نیز به شکل زیر است:
while user_score < 3 and computer_score < 3:
# اجرای یک دور بازیاگر یکی از طرفین به ۳ برسد، شرط حلقه False میشود و مسابقه پایان پیدا میکند.
ساخت تابع اجرای مسابقه
def play_match(target_score=3):
user_score = 0
computer_score = 0
round_number = 1
print(
f"\nبازی شروع شد؛ اولین نفری که به {target_score} امتیاز برسد برنده است."
)
while user_score < target_score and computer_score < target_score:
print(f"\n--- دور {round_number} ---")
user_choice = get_user_choice()
if user_choice is None:
print("از بازی خارج شدید.")
return
computer_choice = random.choice(CHOICES)
print(f"شما: {EMOJIS[user_choice]} {user_choice}")
print(
f"کامپیوتر: {EMOJIS[computer_choice]} {computer_choice}"
)
result = determine_winner(
user_choice,
computer_choice
)
if result == "draw":
print("این دور مساوی شد.")
elif result == "user":
user_score += 1
print("این دور را شما بردید.")
else:
computer_score += 1
print("این دور را کامپیوتر برد.")
print(
f"امتیاز: شما {user_score} - "
f"{computer_score} کامپیوتر"
)
round_number += 1
if user_score > computer_score:
print("\n🎉 شما برنده مسابقه شدید!")
else:
print("\n🤖 کامپیوتر برنده مسابقه شد.")بررسی بخشهای مهم تابع play_match
پارامتر target_score
بهجای اینکه عدد ۳ را در چند نقطه از برنامه ثابت بنویسیم، آن را بهصورت پارامتر تابع تعریف کردهایم. بنابراین بعداً میتوانیم بازی را تا ۵ یا ۱۰ امتیاز اجرا کنیم:
play_match(target_score=5)شماره دور
متغیر round_number فقط برای نمایش بهتر روند مسابقه استفاده میشود. بعد از هر دور یک واحد به آن اضافه میکنیم.
مساوی و امتیاز
در حالت مساوی هیچ امتیازی اضافه نمیشود. فقط وقتی بازیکن یا کامپیوتر برنده شود، امتیاز همان طرف یک واحد افزایش پیدا میکند.
مرحله ششم: امکان شروع بازی جدید
بعد از پایان مسابقه بهتر است کاربر بتواند بدون اجرای دوباره فایل، مسابقه دیگری شروع کند. تابع main() این کار را انجام میدهد:
def main():
print("=== بازی سنگ، کاغذ، قیچی با پایتون ===")
while True:
play_match(target_score=3)
again = input(
"\nدوباره بازی میکنید؟ (بله/خیر): "
).strip()
if again != "بله":
print("بازی تمام شد. موفق باشید!")
breakهر بار که play_match() تمام شود، سؤال شروع مجدد نمایش داده میشود. اگر پاسخ دقیقاً «بله» نباشد، حلقه اصلی متوقف میشود.
کد کامل بازی سنگ، کاغذ، قیچی با پایتون
کد نهایی پروژه به شکل زیر است:
import random
CHOICES = ("سنگ", "کاغذ", "قیچی")
EMOJIS = {
"سنگ": "🪨",
"کاغذ": "📄",
"قیچی": "✂️",
}
def get_user_choice():
while True:
choice = input(
"انتخاب شما (سنگ/کاغذ/قیچی یا خروج): "
).strip()
if choice == "خروج":
return None
if choice in CHOICES:
return choice
print(
"ورودی نامعتبر است. فقط سنگ، کاغذ، قیچی یا خروج را وارد کنید."
)
def determine_winner(user, computer):
if user == computer:
return "draw"
winning_pairs = {
("سنگ", "قیچی"),
("کاغذ", "سنگ"),
("قیچی", "کاغذ"),
}
if (user, computer) in winning_pairs:
return "user"
return "computer"
def play_match(target_score=3):
user_score = 0
computer_score = 0
round_number = 1
print(
f"\nبازی شروع شد؛ اولین نفری که به {target_score} امتیاز برسد برنده است."
)
while user_score < target_score and computer_score < target_score:
print(f"\n--- دور {round_number} ---")
user_choice = get_user_choice()
if user_choice is None:
print("از بازی خارج شدید.")
return
computer_choice = random.choice(CHOICES)
print(f"شما: {EMOJIS[user_choice]} {user_choice}")
print(
f"کامپیوتر: {EMOJIS[computer_choice]} {computer_choice}"
)
result = determine_winner(
user_choice,
computer_choice
)
if result == "draw":
print("این دور مساوی شد.")
elif result == "user":
user_score += 1
print("این دور را شما بردید.")
else:
computer_score += 1
print("این دور را کامپیوتر برد.")
print(
f"امتیاز: شما {user_score} - "
f"{computer_score} کامپیوتر"
)
round_number += 1
if user_score > computer_score:
print("\n🎉 شما برنده مسابقه شدید!")
else:
print("\n🤖 کامپیوتر برنده مسابقه شد.")
def main():
print("=== بازی سنگ، کاغذ، قیچی با پایتون ===")
while True:
play_match(target_score=3)
again = input(
"\nدوباره بازی میکنید؟ (بله/خیر): "
).strip()
if again != "بله":
print("بازی تمام شد. موفق باشید!")
break
if __name__ == "__main__":
main()نمونه اجرای برنامه
=== بازی سنگ، کاغذ، قیچی با پایتون ===
بازی شروع شد؛ اولین نفری که به 3 امتیاز برسد برنده است.
--- دور 1 ---
انتخاب شما (سنگ/کاغذ/قیچی یا خروج): سنگ
شما: 🪨 سنگ
کامپیوتر: ✂️ قیچی
این دور را شما بردید.
امتیاز: شما 1 - 0 کامپیوتر
--- دور 2 ---
انتخاب شما (سنگ/کاغذ/قیچی یا خروج): کاغذ
شما: 📄 کاغذ
کامپیوتر: 📄 کاغذ
این دور مساوی شد.
امتیاز: شما 1 - 0 کامپیوتراشتباههای رایج در این پروژه
انتخاب تصادفی خارج از حلقه
اگر انتخاب کامپیوتر را فقط یک بار قبل از حلقه بسازید، کامپیوتر در تمام دورها همان گزینه را تکرار میکند. random.choice() باید داخل حلقه هر دور اجرا شود.
فراموش کردن حالت مساوی
اگر ابتدا مساوی بودن را بررسی نکنید، ممکن است منطق تشخیص برنده نتیجه اشتباه بدهد. مساوی سادهترین حالت است و بهتر است در ابتدای تابع بررسی شود.
افزایش امتیاز در دور مساوی
در حالت مساوی نباید امتیاز هیچ طرف تغییر کند. افزایش امتیاز فقط در دو شاخه برد بازیکن یا برد کامپیوتر انجام میشود.
پذیرفتن هر ورودی
ورودی باید قبل از استفاده اعتبارسنجی شود. قرار دادن این منطق در تابع جداگانه باعث میشود کد اصلی مسابقه تمیز باقی بماند.
چطور پروژه را حرفهایتر کنیم؟
نسخه فعلی یک بازی کامل متنی است، اما میتوانید آن را در چند مسیر توسعه دهید:
- انتخاب امتیاز هدف: قبل از مسابقه از کاربر بپرسید بازی تا چند امتیاز ادامه پیدا کند.
- ثبت تعداد بردها: تعداد مسابقههای بردهشده کاربر و کامپیوتر را جدا از امتیاز هر مسابقه نگه دارید.
- ذخیره رکورد: آمار برد و باخت را در فایل JSON ذخیره کنید.
- حالت بهترین از N دور: بهجای رسیدن به امتیاز خاص، تعداد دور ثابت تعیین کنید.
- نسخه گرافیکی: بازی را با Tkinter به یک برنامه پنجرهای تبدیل کنید.
- دکمههای تصویری: برای سنگ، کاغذ و قیچی دکمه و تصویر جداگانه طراحی کنید.
- افکت و صدا: در نسخه گرافیکی نتیجه برد و باخت را با صدا یا انیمیشن نمایش دهید.
تمرین پیشنهادی: حالت سنگ، کاغذ، قیچی، مارمولک، اسپاک
اگر میخواهید منطق برنامه را واقعاً تمرین کنید، بهجای فقط کپی کردن کد، دو گزینه جدید به بازی اضافه کنید. در نسخه پنجحالته تعداد روابط برنده بیشتر میشود و مجبور خواهید شد ساختار داده قوانین بازی را توسعه دهید. این تمرین برای درک بهتر Set، Tuple و مدلسازی قانونها بسیار مفید است.
این پروژه چه مفاهیمی را تمرین میدهد؟
- متغیر و ثابتهای برنامه
- Tuple و Dictionary
- Set و عضویت با
in - تابع و پارامتر
- دریافت ورودی
- اعتبارسنجی ورودی
- شرطهای تصمیمگیری
- حلقه while
- ماژول random
- مدیریت وضعیت بازی
- تقسیم برنامه به چند تابع مستقل
جمعبندی
در این آموزش بازی سنگ، کاغذ، قیچی را از یک ایده ساده به یک پروژه کامل پایتون تبدیل کردیم. برنامه انتخاب کاربر را دریافت میکند، انتخاب کامپیوتر را تصادفی میسازد، نتیجه هر دور را با قوانین بازی تشخیص میدهد، امتیازها را نگه میدارد و مسابقه را تا رسیدن یکی از طرفین به امتیاز هدف ادامه میدهد.
ارزش اصلی این پروژه در ظاهر بازی نیست؛ در این است که چند مفهوم پایه پایتون را همزمان در یک ساختار واقعی استفاده میکنید. قبل از رفتن سراغ پروژه بعدی، حداقل یکی از تمرینهای توسعه را خودتان پیادهسازی کنید تا مطمئن شوید منطق برنامه را فهمیدهاید و فقط کد را اجرا نکردهاید.
📌 همچنین بخوانید: تمرین پایتون با جواب، ساخت بازی با پایتون و Pygame و ساخت ربات با پایتون
برای ذخیره پیشرفت در همه دستگاهها و دریافت گواهی پایان مسیر، رایگان ثبتنام کن یا وارد شو.