Hello everyone , I meet you again . After the June day holiday, the company gradually returned to work , Work starts to get busy , So the update frequency has to slow down . And ask brother recently developed small games are original , Though sparrows are small , Five zang organs , Artistic creativity 、 Window layout 、 Code implementation 、 Function debugging , There's nothing missing . So it took a lot of time .
24 Point is a little game that I have always wanted to play , I wanted to do this issue in the text interface , But it's not interesting to find the text interface , So we put . Now come to the graphical interface , Finally, I can realize the way I played as a child , Use playing cards as props . So you'll have to shuffle 、 Licensing 、 Calculate points and other small functions . And let the program judge whether the current four cards can be calculated 24 spot , Behind it is the automatic calculation 24 Point method .( Because brother Wen uses the exhaustive method , There must be a lot of double counting , I dare not pretend here “ Algorithm ” 了 .)
Because there's more content , It's the same as before , Divided into two parts :
Part 1 —— Game interface construction
The next part —— Function code implementation
I believe many people played it when they were young , The rules are also relatively simple : Find a deck of playing cards , Remove the big and small king ,52 card , Draw four cards at random each time , Use the four calculation methods of addition, subtraction, multiplication and division , See who can work out the fastest 24 spot . When I was a child, I didn't have a computer , Therefore, it is impossible to judge whether the four cards have solutions , So as long as everyone gives up the same , You can skip to the next group .
screenshots :
I feel that this small program is more complicated than the previous one , The amount of code has reached 200 lines . The reason is , I want to realize too many functions . Shuffle 、 Licensing 、 Judge whether you can work out 24 spot 、 timing 、 Tips, etc , Brother Wen wanted to be a scoreboard , After the game , The pop-up window shows the correct rate . But in the end, due to lack of energy , Or simply display on the panel “ Tested ”、“ Passed ” Give up . But in fact, this part of the function is relatively simple , Interested friends can freely add in .
Quick calculation 24 The simple flow chart of point is as follows :
The interface of this game is original , Certainly not in line with everyone's aesthetic . After you understand the logic of implementation , You can color it yourself 、 Change pictures 、 Adjust the layout , So as to achieve a satisfactory effect .
Game background 、 The realization of this function , In the last article, I introduced , There's no more verbosity here . Go straight to the code :
from tkinter import *
# Initialize the main window
root = Tk()
root.geometry('800x500+400+200')
root.resizable(0,0)
root.title(' Quick calculation 24 spot ')
# The canvas is the same size as the main window
cv = Canvas(root,width=800,height=500)
# Background image
bg = PhotoImage(file=r"image\poker\bg.png")
cv_bg = cv.create_image(400,250,image = bg)
# Title picture
title = PhotoImage(file=r"image\poker\title.png")
cv_title = cv.create_image(400,60,image = title)
# The canvas is mounted in the window
cv.pack()
root.mainloop()
such , We get an empty window :
Since it is calculated with poker 24 spot , That shuffle 、 Licensing operations are essential . Why did you create such a large window , It is also to leave enough space for licensing .
When the game starts , Or the deck is used up , It's about to start shuffling . So for the convenience of calling , We create a custom function .
import random
def shuffle_card():
global cardnum, back, cv_back
cardnum = list(range(52))
random.shuffle(cardnum)
# A pile of cards that have been shuffled , On the left side of the window
back = PhotoImage(file=r"image\poker\back1.png")
cv_back = cv.create_image(100,200,image = back)
random Module is our old friend , To achieve random effects , We call when shuffling random.shuffle Method , take 52 A digital (0 To 51) The order of is random ,cardnum This list will become an irregularly arranged list . Just right shuffle It means to shuffle the cards , therefore , Use this method here , It's a perfect fit .cardnum Methods need to be called by the main program and other functions , So use global Keyword to declare it as a global variable .
After shuffling the cards , I want to display a picture of the back of a playing card on the left side of the window , There are cards in the stack . Due to limited energy , The thickness of the stack is not realized ( Of course, it is also possible to realize ). To keep the picture on the back of the playing card displayed , You also need to declare it as a global variable .
The effect of the code running is as follows :
We can simulate the action of dealing cards in reality , There are two steps to licensing :1) Show cards at the top of the stack ,2) Move the card to the specified position .
When the front shuffles , We have created a 0 To 51 A disordered list of numbers cardnum, Now we just need to put this 52 Numbers correspond to pictures , You can catch the cards , Automatically display the picture of playing cards .
First , Create a list of poker pictures .
card = [PhotoImage(file=f'image/poker/{
i:0>2}.png') for i in range(1,53)]
Ask brother to prepare the poker picture from 01 Start naming , So you need to use the formatting method , take 01 To 52 The digitally named pictures are read into the list card in .
next , We customize a draw 、 Licensing functions :
import tkinter.messagebox as tm
def draw_card():
draw=[]
if len(cardnum)==0:
tm.showinfo(message=' The deck has been used up , Reshuffle your cards ')
shuffle_card()
for i in range(4):
# Simulate a draw : Deck cardnum A card pops up at the end , Put it in the list of cards to show draw in
draw.append(cardnum.pop())
# Show cards at the top of the stack
cv_card.append(cv.create_image(100,200,image=card[draw[i]]))
# If you finish the last four cards , Delete the picture on the back of the card ( Detail control )
if len(cardnum)==0:cv.delete(cv_back)
# call canvas Of move Method , Move the card to the specified position , Achieve licensing effect
for _ in range(150*(i+1)):
cv.move(cv_card[i],1,0)
cv.update()
There are several details :
The effect of the final deal is as follows :
When drawn out 4 After playing cards and showing them , We first have to let the program calculate , These four cards are combined in various permutations , Can you come to 24 spot . The calculation method of this part will be introduced in the next chapter . If there is a definite solution , It can be concluded that 24 spot , We're about to start timing .
Brother Wen introduced the making of timers in his previous article , Here you can take it directly . Of course , Or default 90 Second countdown .
Python Animation production :90 Second countdown round progress bar effect
The specific code has been explained in this article , So I won't be too verbose here . But the code here also solves the last question in that little article : If you achieve a smooth progress bar .
The code is as follows :
def initialize():
global angle,count,cv_arc,cv_inner,cv_text
count=90
angle=360
cv_arc=cv.create_oval(100,330,200,430,fill='red',outline='yellow')
cv_inner=cv.create_oval(120,350,180,410,fill='yellow',outline='yellow')
cv_text=cv.create_text(150,380,text=count,font =(' Microsoft YaHei ',20,'bold'),fill='red')
draw_card()
def countdown():
global angle,count,cv_arc,cv_inner,cv_text,cd
if angle == 360:
angle -= 1
else:
cv.delete(cv_arc)
cv.delete(cv_inner)
cv.delete(cv_text)
cv_arc=cv.create_arc(100,330,200,430,start=90,extent=angle,fill="red",outline='yellow')
angle -= 1
if angle%4 == 0: count-=1
cv_inner=cv.create_oval(120,350,180,410,fill='yellow',outline='yellow')
cv_text=cv.create_text(150,380,text=count,font =(' Microsoft YaHei ',20,'bold'),fill='red')
if count==0:
tm.showinfo(message=' The countdown is over ! Automatically enter the next round ')
cv.delete(cv_arc)
cv.delete(cv_inner)
cv.delete(cv_text)
initialize()
else:
cd = root.after(250,countdown)
in addition , Brother Wen has defined another name here initialize() Function of , The translation is initialization , The purpose is to put some repetitive work into it , For example, the circle that blocks the progress bar . therefore , We can put the card grabbing function draw_card() Put it inside , In the main program, you only need to call initialize() that will do .
The effect is as follows :
There are many ways to make it possible for players to enter answers from the keyboard , I would like to take this opportunity to introduce a “ Event binding " Methods .
First , Create a Label Tag components , Same as Button Button components 、Canvas Canvas components , The label component is also tkinter The next component , It is easy to understand why Canvas The same level . To be in Canvas Display components of the same level on the , We need to use the create_window Method .
answer=StringVar()
cv.create_text(400,350,text=' Please enter your answer ',font =(' Square regular script, simplified Chinese ',18,'bold'))
lb = Label(root,text='',font=(' Microsoft YaHei ',15),textvariable=answer,bg='lightyellow')
cv_lb = cv.create_window(400,400,window=lb)
# Binding gets input from the keyboard <Key>, And pass it to the user-defined function myanswer
lb.bind('<Key>',myanswer)
# Get the tag component into focus , Otherwise, you cannot input from the keyboard
lb.focus_set()
First of all, here is a definition of tkinter Under the StringVar Class , In fact, it represents a string , But more than ordinary string variables “ intelligence ”. such , When we're in Label Use... In components textvariable Parameters , After specifying this instance , It can be dynamically bound together . in other words StringVar Into what content ,Label The component will be displayed automatically , It is much simpler and more convenient than the assignment operation .
Create good Label After label assembly , You can use bind() Method to bind a Key event , Represents the input content of the keyboard , Then the input is implicitly passed to myanswer This custom callback function , This function is used to assign the keyboard input to StringVar, And then in Label It shows that .
The function is defined as follows :
trans={
'plus':'+','minus':'-','asterisk':'*','slash':'/','parenleft':'(','parenright':')'}
def myanswer(event):
s=event.keysym
txt=answer.get()
if s=='BackSpace':
txt=txt[:-1]
elif s=='Return':
if is_right(txt):
root.after_cancel(cd)
c = tm.askyesno(message=' Go on to the next game ?')
if c:
cv.delete(cv_arc)
cv.delete(cv_inner)
cv.delete(cv_text)
initialize()
else:root.destroy()
else:
txt=''
elif s.isnumeric():
txt+=s
elif s in trans:
txt+=trans[s]
answer.set(txt)
event Is a variable implicitly passed in by the event binding function , and event.keysym It represents the current ( Trigger ) The characters obtained by this function , That is, which key was pressed from the keyboard . Be careful , Just press one key here , It will trigger myanswer Callback function , therefore event.keysym The value of represents only one character at a time .
But the display form of this character is different from what we usually think . In addition to single character keys such as numbers and letters , Direction key 、 Symbols and so on are all expressed in one word , and event.keysym It's the same word , Not the arrow keys 、 Symbols and other special characters . therefore , Let's create a dictionary here trans, Come and take event.keysym The resulting symbol , Like addition, subtraction, multiplication and division 、 Parentheses, etc , Convert to correct characters + - * / ( ) Show it .
We also need to limit the characters that players can enter , Except for numbers and arithmetic symbols + 0 * / ( ), There are also carriage return and backspace ( Delete ), Players should not react when they press any other key .
Each time the player enters a character , All use answer.get() Method to get the current Label Characters on , Then carry out corresponding operations ( Delete 、 Add characters ), Finally through answer.set() Method to display the new string in the Label On .
besides , This function also contains a function ,is_right(), When the player presses enter (Return) When , This function is used to judge the current Label Whether the characters on the can calculate 24, To judge the outcome . If you can , Then cancel the timer (cd), Then start the next round , If not , be “ wipe ” Drop the current answer , Ask the player to re-enter .
is_right() Functions are part of the code implementation , But brother Wen thinks this function is related to the callback function above , I decided to introduce it in the last chapter .
In addition to determining whether it can calculate 24 outside , Also make sure that players only use , And used up the given 4 A digital . Then the arithmetic symbols in the resulting equation are removed , Then divide it into numbers , Just match the current deck number nums contrast , If different , It means that the player did not use the correct number .
To prevent players from entering the denominator 0 And so on , Use here try…except Method to judge .eval() Function is used to convert the string of an arithmetic expression into a real expression for calculation , And if the denominator is 0, More or less parentheses , Wait, the expression is wrong , An error prompt will be thrown directly .
import math
def is_right(txt):
try:
result = eval(txt)
except:
tm.showinfo(message=' The formula is incorrect , Please re-enter !')
return False
for i in '+-*/()':
txt=txt.replace(i,' ')
txt=[int(i) for i in txt.split()]
if sorted(txt)!=sorted(nums):
tm.showinfo(message=' Please use the given number !')
return False
if math.isclose(result,24):
tm.showinfo(message=' Congratulations ! Correct answer !')
return True
Last but not least , Because we're calculating 24 In the process of dot , It is quite possible to use division , And once you use division , The result must be a floating point number . Because computers use binary to represent floating point numbers , So once some decimals can not be completely expressed in binary , There will be an infinite circular decimal , Produce errors . So here, if you directly use if result==24 To judge , In some cases, you will not get the correct results , such as 23.999999 and 24 Is not equal , but 23.999999 It may be caused by division in the calculation process .
So we use math Modular isclose Method .math.isclose(result, 24) It means that the difference between two numbers is in a very small range , The two numbers are the same .
The final effect is as follows :