شاید تا الان بارهای بازی دوز را بازی کردهاید. اما آیا برای پیادهسازی این بازی با زبان برنامه نویسی پایتون تلاش کردهاید؟ بازی دوز یکی از محبوبترین و مشهورترین بازیهای ساده، دو نفره و تقریبا مشهور در سراسر جهان است. در این مطلب از مجله فرادرس، ساخت بازی دوز با پایتون را به صورت کامل به عنوان پروژه تمرینی پیادهسازی کردهایم. برای اینکار از تمام دانش پایتونی که تا به اینجا بدست آوردهایم استفاده خواهیم کرد. بازی دوز، بازی دونفره است که بر بروی صفحه ۳*۳ بازی میشود. هر بازیکنی بین X و O انتخاب میکند. بازیکن اول علامت X را بر روی یکی از خانههای خالی صفحه بازی رسم میکند و بازیکن دوم بعد از او علامت O را بر روی یکی دیگر از خانههای صفحه بازی رسم میکند. این کار تا زمانی اتفاق میافتد که بازیکنی بتواند خط کاملی را بر روی صفحه بازی رسم کند و برنده شود در غیر این صورت بازی بهصورت مساوی به پایان میرسد.
در این مطلب درباره ساخت بازی دوز با پایتون صحبت میکنیم. در ابتدا فرایند این کار را بررسی کرده و سپس شروع به پیادهسازی بازی دوز میکنیم. فرایند پیادهسازی را نیز به دو مرحله ساخت صفحه بازی و افزودن منطق بازی تقسیم کردهایم. در هر مرحله همراه با توضیحات کدهای مربوطه را نیز نمایش دادهایم.
فرایند ساخت بازی دوز با پایتون چیست؟
برای پیادهسازی این پروژه پایتونی، از تمام دانش آموخته خود تا به اینجا استفاده میکنیم و مرحله به مرحله فرایند ساخت بازی دوز با پایتون را با نمایش کد و مثال نشان دادهایم. برای اجرای این پروژه مراحل ساخت بازی دوز را به صورت مرحله به مرحله، جداگانه تقسیم کردهایم تا درک فرایند ساخت بازی به صورت موازی را آسانتر کنیم. فرایند ساخت بازی دوز در پایتون به صورت کلی شامل موارد فهرست شده در زیر میشود.
- درک روش اجرای بازی دوز در پایتون
- ساخت صفحه بازی
- ساخت دکمههای بازی
- افزودن منطق بازی
- تشخیص برنده و مساوی بازی
- تعبیه دکمهای برای بازگرداندن بازی به حالت اولیه
بازی دوز در پایتون که با نامهای هیچها و صلیبها یا X و O نیز شناخته میشود، بازی ساده دونفرهای است که در آن هر کدام از بازیکنها برای انتخاب X و O آزاد هستند. این بازی بر روی صفحه سه در سهای انجام میگیرد که مجموعا ۹ خانه دارد. به نوبت هر بازیکنی این فرصت را دارد که نماد مربوط به خود را در یکی از خانههای خالی صفحه بازی رسم کند. به محض اینکه یکی از بازیکنان موفق به رسم خط صافی با نماد خودش به صورت عمودی، افقی یا قطری شد یعنی که بازیکن مورد نظر برنده بازی شده است. اما اگر همه خانههای بازی پُر از علامت شدند و هیج خطی رسم نشده بود، درنتیجه بازی به صورت مساوی به پایان میرسد.
روش اجرای بازی دوز در پایتون
در بازی دوزی که میخواهیم بسازیم به دو بازیکن نیاز خواهیم داشت. این بازیکنان علامتهای مورد نظر خود را انتخاب میکنند. این علامتهای مورد استفاده در بازی دوز X و O هستند. دو بازیکن علامتهای X و O را بر روی فضای ۳*۳ با ۹ خانه خالی مطابق شکل زیر رسم میکنند.
بازی با رسم اشکال X و O بر روی خانههای خالی صفحه بازی توسط بازیکنان اجرا میشود. بازیکنی که علامت X را انتخاب کند بازی را شروع میکند و بعد از آن نوبت به بازیکن بعدی میرسد که علامت O را بر روی خانهی خالی از صفحه بازی رسم کند. و به همین ترتیب تا رسیدن به نتیجه به نوبه خانههای صفحه بازی را با علامتها مختص خود پُرمیکنند.
در ساخت بازی دوز با پایتون از یکی از کتابخانههای گرافیکی پایتون به نام Tkinter استفاده میکنیم. کتابخانه Tkinter یکی از پکیج های گرافیکی یا GUI استاندارد پایتون است که به صورت خودکار، همزمان با نصب پایتون بر روی سیستم نصب میشود.
در صورتی که نسبت به کار با کتابخانه گرافیکی پایتون به نام Tkinter نا آشنا باشید و تمایل به یادگیری این کتابخانه دارید با مراجعه به فیلم آموزشی آموزش پروژه محور کتابخانه Tkinter در پایتون از وبسایت آموزشی فرادرس، تا حد بسیار خوبی بر توابع و تکنیکهای کاربردی این کتابخانه استاندارد پایتون مسلط میشوید.
در این پروژه فقط نیاز به وارد کردن دو تا از ماژول های پایتون داریم.
1from tkinter import *
2import random
در ادامه به تعریف توابعی پرداختهایم که به کمک آنها فرایند اجرای پروژه را بدون روبهرو شدن با خطایی میتوانیم به سرانجام برسانیم. کلمه کلیدی pass نقش «نگهدارنده فضای خالی» (Placeholder) را بازی میکند. این کلمه کلیدی به برنامه نویسان کمک میکند که بدون نوشتن کد خاصی در توابع به ریختن طرح کلی پروژه بپردازند.
افزایش مهارت های کار با پایتون با کمک فرادرس
در صورتی که نسبت به زبان برنامهنویسی پایتون آشنایی کمی دارید و علاقهمند به شروع آموزش این زبان برنامهنویسی قدرتمند و انعطافپذیر هستید میتوانید از فیلم آموزش رایگان پایتون در ۱۴۰ دقیقه فرادرس استفاده کنید. اما اگر مراحل مقدماتی و آشنایی نسبتا ابتدایی با این زبان برنامهنویسی را پشت سر گذاشتهاید، بهتر است که به سراغ فیلمهای آموزشی تخصصی پایتون در فرادرس بروید.
وبسایت آموزشی فرادرس، فیلمهای بسیار خوب و با کیفیتی را برای آموزش مفاهیم سطح بالای زبان پایتون تهیه کرده است که چند مورد را در ادامه فهرست کردهایم.
برای مشاهده گزینههای بیشتر میتوانید به وبسایت فرادرس مراجعه کنید یا با کلیک بر روی تصویر بالا به صورت مستقیم به صفحه مخصوص فیلمهای آموزش زبان برنامهنویسی پایتون رفته و هر کدام از فیلمهای مورد نیاز خود را مشاهده کنید.
مرحله اول ساخت صفحه بازی
اولین مرحله ساخت بازی دوز با پایتون، مرحله سنگین کار با کتابخانه Tkinter است. زیرا کتابخانه Tkinter شکل صفحه بازی و عملکرد آن را تعیین میکند.
صفحه بازی را به عنوان نمونهای از کلاس چهاچوب مخصوص tkinter
تعریف میکنیم. کدنویسی این عملیات را با عبارت board = Tk()
انجام میدهیم. با استفاده از قطعه کد board.title(“Tic-Tac-Toe”)
در برنامه تیتر مناسبی نیز برای صفحه بازی انتخاب میکنیم.
تعریف نماد برای ساخت بازی دوز با پایتون
مرحله بعدی ساخت لیستی از کاراکترها است. در لیستی که تعریف کردیم حروف الفبای X و O را قرار دادهایم. قطعه کد مربوط به تعریف لیست به شکل characters = [“x”, “o”]
است. سپس خط کد board.mainloop()
را به پایین فایل کدهای خود اضافه میکنیم. این قطعه کد به عنوان آخرین خط در کدها باقی خواهد ماند. دستور board.mainloop()
صحفه بازی را باز و آماده شنیدن دستورات نگهمیدارد.
در این لحظه از زمان کدهای نوشته شده به شکل زیر خواهند بود.
1from tkinter import *
2import random
3
4def restart():
5pass
6
7def next_turn():
8pass
9
10
11
12
13board = Tk()
14board.title("Tic-Tac-Toe")
15characters = ["x","o"]
16
17
18
19
20
21
22
23board.mainloop()
در مرحله بعدی به تعریف متغیر بازیکن خواهیم پرداخت. این متغیر تعیین میکند که الان برای بازی نوبت کدام بازیکن است. متغیر بازیکن از ماژول random
در پایتون برای تعیین بازیکن شروع کننده بازی به صورت تصادفی استفاده میکند. کد مربوط به این عملیات به صورت player = random.choice(characters)
است.
دکمهها را با کمک آرایه دوبعدی یا لیستی از لیستها ایجاد میکنیم. از آنجا که هنوز برای دکمههای صفحه بازی، هیچ مقداری مشخص نشده است، باید مقدار مربوط به فضای خالی را به هر دکمه تخصیص بدهیم. فرق خاصی نمیکند، در صورتی که این برنامه را برای خودتان بازنویسی میکنید نیازی به تخصیص فضای خالی به اجبار برای دکمهها نیست. به عنوان مقدار اولیه میتوانید هر مقدار مد نظرتان را به این دکمههای آرایه دو بعدی اختصاص دهید. در ادامه مراحل بازی همزمان با انتخاب بازیکنان مقادیر جدید را به این دکمهها اختصاص خواهیم داد.
1buttons = [["", "", ""]
2
3 ["", "", ""]
4
5 ["", "", ""]]
در ادامه میخواهیم نوار مربوط به وضعیت را ایجاد کنیم. در این نوار به بازیکنان میگوییم که الان نوبت کیست و دکمهای را نیز برای شروع بازی جدید قرار خواهیم داد. «نوار وضعیت» (Status Bar) را با استفاده از ویجت برچسبهای کتابخانه tkinter
ایجاد میکنیم.
1label = Label(text=player+ "’s turn", font=("futura",40))
2
3label.pack(side="top")
ساخت دکمه بازی جدید شبیه به کدی خواهد بود که در ادامه آمده است. عبارت command=restart
در این کد، فرمانی است که با کلیک هر بازیکن بر روی دکمه در هر زمان، بازی جدیدی را شروع میکند.
1new_game_button = Button(text="New Game", font=("futura",20), command=restart)
2
3new_game_button.pack(side="top")
همچنین به ساختن چهارچوبی برای تخصیص دادن هر دکمه به محل صحیح قرارگیری آن بر روی صفحه نیاز داریم. با استفاده از کد زیر چهارچوب مورد نیاز را ایجاد میکنیم.
1frame = Frame(board)
2
3frame.pack()
در نهایت صفحه ایجاد شده شبیه به تصویر زیر خواهد بود.
ساخت دکمه های بازی
الان زمان ساختن دکمهها است. ساختن دکمهها نیاز به استفاده از حلقههای تودرتو دارد. حلقه خارجی ایندکس ردیفها را مشخص میکند و حلقه داخلی ایندکس مربوط به ستون را در آرایه دو بعدی گفته شده تعریف میکند. بلاک کدی که درون حلقهها قرار دارد دکمهها را ایجاد میکند، ویژگیهای ضروری را به آنها تخصیص میدهد و آنها را به صفحه بازی اضافه میکند. دکمهها حاوی دو تابع هستند. تابع اول از جنس توابع lambda است که ایندکس مربوط به سطر و ستون خود را به متغیرهایی با نام row
و column
اختصاص میدهد. خروجی این توابع را میتوان به تابع دوم یعنی next_turn()
ارسال کرد.
حلقههای دورنی و بیرونی به صورت هاردکد به اندازه ۳ پیمایش محدود شدهاند. زیرا سطرها و ستونهای صفحه شامل ۳ مربع در هر کدام هستند. هر سطر و ستون با ایندکس صِفر شروع میشود.
در پایین کدهای مربوط به بلاک حلقهها را پیادهسازی کردهایم.
1for row in range(3):
2 for column in range(3):
3 buttons[row][column] = Button(frame, text ="",font=('consolas',40), width=5, height=2, command=lambda row=row, column=column:next_turn(row,column) )
4 buttons[row][column].grid(row=row,column=column)
در پایانِ عملیاتِ ایجاد صفحه بازی در پروژه ساخت بازی دوز با پایتون، کد نوشته شده به شکل آمده در پایین است.
1from tkinter import *
2
3import random
4
5
6def restart():
7
8pass
9
10
11def next_turn():
12
13pass
14
15
16def check_winner():
17
18pass
19
20
21def empty_spaces():
22
23pass
24
25
26board = Tk()
27
28board.title("Tic-Tac-Toe")
29
30characters = ["x","o"]
31
32player = random.choice(characters)
33
34
35buttons = [['','',''],
36
37['','',''],
38
39['','','']]
40
41
42label = Label(text=player+ "'s turn", font=("futura",40))
43
44label.pack(side="top")
45
46
47
48
49new_game_button = Button(text="New Game", font=("futura",20), command=restart)
50
51new_game_button.pack(side="top")
52
53
54frame = Frame(board)
55
56frame.pack()
57
58
59for row in range(3):
60
61 for column in range(3):
62
63 buttons[row][column] = Button(frame, text ="",font=('consolas',40), width=5, height=2, command=lambda row=row, column=column:next_turn(row,column) )
64
65 buttons[row][column].grid(row=row,column=column)
66
67
68board.mainloop()
و صفحه بازی ساخته شده نیز به شکل زیر است.
مرحله دوم افزودن منطق بازی
تابع next_turn()
که در بلاک کد درون حلقههای تودرتو تعریف شده بود اولین تابعی منطقی است که در این بخش تعریف کردهایم.
در صورت علاقهمند بودن به مطالعه بیشتر درباره حلقههای پایتونی، میتوانید با مراجعه به مطلب «تکرارها و حلقه ها» (Iterations and Loops) در پایتون به زبان ساده از مجله فرادرس اطلاعات بیشتری نیز کسب کنید.
هدف تابع ()next_turn()
بررسی پیروز شدن یکی از بازیکنان است. اگر هیچ کدام از بازیکنان پیروز نشده بود، پس نوبت به حرکت بازیکن بعدی میرسد. اما این تابع چگونه کار میکند؟ نباید فراموش کنیم که پارامترهای سطر و ستون را به این تابع اضافه کنیم.
1def next_turn(row, column):
2
3#the global keyword allows us to access and change the global variable
4
5global player
6
7
8# this general logic applies to both players. If the button’s text is blank and there is no winner then….
9
10 if buttons[row][column]['text'] == "" and check_winner() is False:
11
12
13 #if it’s player one’s turn, the button’s text is updated to match player one’s character, checks for a winner and performs the appropriate actions
14
15 if player == characters[0]:
16
17 buttons[row][column]['text'] = player
18
19 if check_winner() is False:
20
21 player = characters[1]
22
23 label.config(text=(characters[1] +" turn"))
24
25
26
27 elif check_winner() is True:
28
29 label.config(text=(characters[0] +" won"))
30
31
32 elif check_winner() == "Tie":
33
34 label.config(text=("Tie"))
35
36
37
38 #the logic repeats again for the other player
39
40
41 else:
42
43 buttons[row][column]['text'] = player
44
45 if check_winner() is False:
46
47 player = characters[0]
48
49 label.config(text=(characters[0] +" turn"))
50
51
52 elif check_winner() is True:
53
54 label.config(text=(characters[1] +" won"))
55
56
57 elif check_winner() == "Tie":
تابع check_winner()
تابع بعدی است که باید تعریف شود. در این تابع میخواهیم که هر ردیف، ستون و قطر را به منظور پیدا کردن پیروز یا مساوی بررسی کنیم. کد نویسی فرایندی که گفتیم به صورت زیر است.
1for row in range(3):
2
3 #if all the buttons in a row have matching text that is not equal to a blank space
4
5 if buttons[row][0]['text'] == buttons[row][1]['text'] == buttons[row][2]['text'] != '':
6
7 return True
8
9
10for column in range(3):
11
12 #if all the buttons in a row have matching text that is not equal to a blank space
13
14 if buttons[0][column]['text'] == buttons[1][column]['text'] == buttons[2][column]['text'] != '':
15
16 return True
17
18
19 #since the grid is small and unchanging we can hardcode the diagonals
20
21 if buttons[0][0]['text'] == buttons[1][1]['text'] == buttons[2][2]['text'] != "":
22
23 return True
24
25
26 if buttons[2][0]['text'] == buttons[1][1]['text'] == buttons[0][2]['text'] != "":
27
28 return True
29
30
31 #then we check for ties
32
33 if empty_spaces() is False:
34
35 return "Tie"
36
37
38 else:
39
40 return False
در این لحظه از زمان، میتوانیم بر روی مربعها کلیک کنیم. میبینیم که کاراکترها بر روی دکمهها ظاهر میشوند و محاسبات لازم برای پیدا کردن پیروز احتمالی انجام میپذیرد. در این مرحله صفحه بازی به شکل نشان داده شده در پایین است.
تشخیص مساوی شدن در بازی
برای تشخیص مساوی شدن بازی، لازم است که تابع empty_spaces()
را تعریف کنیم. وقتی که تابع empty_spaces()
فراخوانی شود، متن تمام دکمهها را بررسی میکند. این تابع، خالی بودن یا کاراکتر داشتن هر دکمه بازی بررسی میکند. اگر محتوای دکمهای شامل کاراکتر باشد، این تابع از تعداد کل متغیرهای spaces
یک واحد کم میکند.
1def empty_spaces()
2
3 spaces = 9
4
5
6 for row in range(3):
7
8 for column in range(3):
9
10 #checks to see if the button’s text is empty and decrements if it is
11
12 if buttons[row][column]['text'] != "":
13
14 spaces-=1
15
16 if spaces == 0
17
18 return False
19
20 else:
21
22 return True
با اضافه شدن حرکات بازیکنان و به تساوی کشیده شدن فرضی، صفحه بازی به این شکل در میآید.
تعبیه دکمه restart برای بازگرداندن بازی به حالت اولیه
آخرین کاری که در ساخت بازی دوز با پایتون، باید قبل از اعلام تکمیل پروژه انجام بدهیم، ساخت تابع restart()
است. این تابع، متغیر سراسری بازیکنان را بهروزرسانی میکند، متن نوار وضعیت بازیکنان را تنظیم میکند و متن همه دکمهها را با فضای خالی بازنشانی میکند.
1def restart()
2
3global player
4
5 #updates the player order and text bar
6
7 player = random.choice(characters)
8
9 label.config(text=player+ " ‘s turn")
10
11
12 #updates button text to blank spaces
13
14 for row in range(3):
15
16 for column in range(3):
17
18 buttons[row][column].config(text="")
تعریف تابع restart()
تکمیل کننده این متن آموزشی درباره ساخت بازی دوز با پایتون است.
کد کامل ساخت بازی دوز با پایتون
در بخش زیر تمام کد مربوط به پیادهسازی این بازی را نمایش دادهایم. با کپی کردن و بازنشانی این کد در ادیتور خود میتوانید برنامه نوشته شده را بهطور کامل در سیستم شخصیتان ایجاد کنید.
1from tkinter import *
2
3import random
4
5
6def restart():
7
8 global player
9
10 #updates the player order and text bar
11
12 player = random.choice(characters)
13
14 label.config(text=player+ " 's turn")
15
16
17 #updates button text to blank spaces
18
19 for row in range(3):
20
21 for column in range(3):
22
23 buttons[row][column].config(text="")
24
25
26
27def next_turn(row, column):
28
29 global player
30
31 if buttons[row][column]['text'] == "" and check_winner() is False:
32
33 if player == characters[0]:
34
35 buttons[row][column]['text'] = player
36
37
38 if check_winner() is False:
39
40 player = characters[1]
41
42 label.config(text=(characters[1] +" turn"))
43
44
45 elif check_winner() is True:
46
47 label.config(text=(characters[0] +" won"))
48
49
50 elif check_winner() == "Tie":
51
52 label.config(text=("Tie"))
53
54
55 else:
56
57 buttons[row][column]['text'] = player
58
59 if check_winner() is False:
60
61 player = characters[0]
62
63 label.config(text=(characters[0] +" turn"))
64
65
66 elif check_winner() is True:
67
68 label.config(text=(characters[1] +" won"))
69
70 elif check_winner() == "Tie":
71
72 label.config(text=("Tie"))
73
74
75
76def check_winner():
77
78 for row in range(3):
79
80 if buttons[row][0]['text'] == buttons[row][1]['text'] == buttons[row][2]['text'] != '':
81
82 return True
83
84 for column in range(3):
85
86 #if all the buttons in a row have matching text that is not equal to a blank space
87
88 if buttons[0][column]['text'] == buttons[1][column]['text'] == buttons[2][column]['text'] != '':
89
90 return True
91
92 if buttons[0][0]['text'] == buttons[1][1]['text'] == buttons[2][2]['text'] != "":
93
94 return True
95
96 if buttons[2][0]['text'] == buttons[1][1]['text'] == buttons[0][2]['text'] != "":
97
98 return True
99
100 #then we check for ties
101
102 if empty_spaces() is False:
103
104 return "Tie"
105
106 else:
107
108 return False
109
110
111
112def empty_spaces():
113
114 spaces = 9
115
116 for row in range(3):
117
118 for column in range(3):
119
120 if buttons[row][column]['text']!='':
121
122 spaces-=1
123
124 if spaces == 0:
125
126 return False
127
128 else:
129
130 return True
131
132
133board = Tk()
134
135board.title("Tic-Tac-Toe")
136
137characters = ["x","o"]
138
139
140player = random.choice(characters)
141
142buttons = [['','',''],
143 ['','',''],
144 ['','','']]
145
146label = Label(text=player+ "'s turn", font=("futura",40))
147
148label.pack(side="top")
149
150
151new_game_button = Button(text="New Game", font=("futura",20), command=restart)
152
153new_game_button.pack(side="top")
154
155
156frame = Frame(board)
157
158frame.pack()
159
160
161for row in range(3):
162
163 for column in range(3):
164
165 buttons[row][column] = Button(frame, text ="",font=('consolas',40), width=5, height=2, command=lambda row=row, column=column:next_turn(row,column) )
166
167 buttons[row][column].grid(row=row,column=column)
168
169
170board.mainloop()
آموزش تخصصی پایتون با استفاده از تمرینات پروژه محور
آموزش زبانهای برنامه نویسی شامل مراحل مختلفی میشود. از مراحل پایه و مقدماتی برای آشنایی با سینتکس زبان و مفاهیمی مانند حلقهها، نوعهای داده، عبارات شرطی و غیره گرفته تا مراحل پیشرفته و مفاهیم تخصصی مانند شیگرایی را شامل میشود. اما معمولا آخرین مرحله برای آموزش برنامهنویس به منظور ورود به دنیای واقعی و بازار کار، اجرای تمرینات پروژه محوری است که قابل ارائه در دنیای واقعی باشند. از طراحی سایت گرفته تا طراحی مدلهای هوش مصنوعی و ساخت اپلیکیشنهای کاربردی، دورههایی هستند که هر کدام مهارتهای مخصوص به خود را نیاز دارند.
حتی برای اجرای بعضی از پروژهها لازم است که از چند زبان برنامهنویسی و چند تکنولوژی مختلف در کنار یکدیگر استفاده کنیم. در این بخش چند مورد از فیلمهای آموزشهای حرفهای پروژه محور پایتون را که توسط فرادرس آماده شده معرفی میکنیم. فرادرس برای تهیه این فیلمهای آموزشی حداکثر حساسیت را بهکار برده تا برای شما مخاطبان عزیز، مفیدترین گزینههای ممکن را فراهم کند.
در صورت تمایل با کلیک بر روی تصویر بالا، به فیلمهای آموزشی بیشتری دسترسی خواهید داشت.
جمع بندی
بازی دوز در پایتون که به نام X و O نیز شناخته میشود، یکی از سادهترین بازیهای دونفرهای است که هر دو بازیکن علامت مورد نیاز خود را از بین X و O انتخاب میکنند و بر روی صفحهای ۳*۳ به اجرای بازی میپردازند. وقتی یکی از بازیکنان پیروز میشود که موفق شود در یکی از راستاهای عمودی افقی یا قطری کاراکترهای خود را به صورت ردیفی قرار دهد. در غیر این صورت اگر همه خانههای صفحه بازی پر از کاراکتر شود و کسی موفق به ترسیم خطی با نماد خود نشود بازی به صورت مساوی به پایان میرسد.
در این مطلب از مجله فرادرس، این بازی را با کمک زبان برنامهنویسی پایتون و کتابخانه گرافیکی Tkinder به صورت کاربر پسندی پیادهسازی کردیم. تمرین پیادهسازی این بازی یا افزودن نکاتی برای سرگرم کنندهتر شدن یا حتی بزرگ کردن بازی به منظور سختتر کردن بازی یکی از راههای افزایش مهارتهای پایتونی هستند.
source