Dou landlords should not be unfamiliar to everyone, the following article is mainly to share with you about the use of Python to crack the landlord's pieces of relevant information, the article introduced in very detailed, for everyone has a certain reference learning value, the need for friends below to see it together.
Objective
I believe everyone has played the landlord, the rules are no longer introduced.
Directly on a circle of friends to see the pieces of the picture:
This problem I just saw, had tried to crack by hand, each time thought to find the farmer's winning strategy, finally found that farmers can not escape. Because the manual crack can not exhaust all the possibilities, so this question whether farmers have hands run away, only through the code to help us to calculate.
This article will briefly explain how to solve such problems through code, and finally publish the final results of the endgame, and open source code for everyone to spit out the slots.
Minimax
The core idea of the code is minimax. Minimax can be disassembled into two parts, Mini and Max, respectively, with the meaning of the smallest and largest.
What is the intuitive understanding? It's kind of like A and B two people playing chess. A can now be moves at n points, suppose A is moves at a certain point, which makes the panel evaluation score of this step of a is the highest, but when it is under B, it is bound to go in the most unfavorable direction of a, so that the next step of a is bound to follow the trajectory of B set, and cannot reach the highest level score of a at the first step.
In the hands of the same, if the farmer's hand, so that landlords can no matter how to win, then the farmers have a winning strategy, otherwise, farmers will lose.
Core Logic
We can use a function hand_out to simulate a person's hand-out process. In real life, a person who wants to make a card, must know all the cards in his hand: me_pokers, also need to know the hands of the cards: Last_hand. If we want to use this function to simulate two people's cards, you also need to know the opponent's current cards: enemy_pokers.
The return value of this function is the ability to win when it is my turn to me_pokers the card. If you can win then return true, otherwise return false.
def hand_out (Me_pokers, Enemy_pokers, Last_hand)
Assuming my hand is out, if my hands are out, then I will know immediately that I have won, and if the opponent's cards are out and I am not, then I fail.
If not Me_pokers:return Trueif not Enemy_pokers:return False
Because now it's my turn to make a card, so I need to know all the hand combinations I can make right now. Note: In this combination, the strategy of having a card (i.e. no cards) is included.
All_hands = Get_all_hands (me_pokers)
Now we're going to traverse all the possible hand combinations.
First of all, I need to know what the cards are on the other hand.
If the other side of the hand to choose a card, or do not have a hand, then I must not be a round of cards, but I can make any card
If the opponent is out of the hand, then I have to make a bigger card than it or choose this round of direct card (no card)
The key point comes, after I have finished my cards or choose to pass the card, we need to use a recursive adjustment to simulate the opponent's next behavior. If the next time the opponent does not win the card, then I will win this time, otherwise, for each of my choice of cards, the opponent can win, then I am defeated.
The full code is as follows:
def hand_out (Me_pokers, Enemy_pokers, Last_hand, cache): If not me_pokers: # I'm all over the cards, straight wins return True if not enemy_p Okers: # Opponents all over the card, I failed to return False # Get all the hand combinations I can currently make, including the card all_hands = Get_all_hands (me_pokers) # traverse all my card combinations, Perform a simulated card for hand in All_hands: # If the last opponent is out of the cards, then this round I have to have a bigger card than the opponent or the opponent on the last round of the choice of cards, then I only need to out of any card, but not the card if (Last_hand and Can_ COMB2_BEAT_COMB1 (Last_hand, hand)) or (not Last_hand and hand[' type ']! = Comb_type. PASS): # Simulate opponent out of the card, if the opponent can not win, then I will win if not hand_out (Enemy_pokers, Make_hand (me_pokers, hand), hand, cache): return True # If the previous round opponent is out of hand, but I have chosen this round of cards elif Last_hand and hand[' type '] = = Comb_type. PASS: # simulates the opponent's card, if the opponent can not win, then I will win if not hand_out (Enemy_pokers, Me_pokers, None, cache): return True # If all previous combinations of cards cannot win, then I will fail to return False
Build
Once the above core logic is clear, building a cracker will be very simple.
First, we want to use numbers to represent the size of the cards, here we use 3 to represent 3, 11来 means Q, and so on ...
Secondly, we need to find out a hand of all the card combination, here need get_all_hands
function, concrete implementation is cumbersome but very simple, do not repeat here.
Then, we also need a card force judgment function can_comb2_beat_comb1(comb1, comb2)
, which is used to compare the hand force of the two groups of hands to see if COMB2 can beat COMB1. The only thing to note, in the rules of the landlord, in addition to bombs, all the other cards are equal, only the card type to compare.
Finally, we need a simulation of the card function make_hand(pokers, hand)
, used to find out in the case of hand for the pokers of the hand, the rest of the hand, the implementation is very simple, simply remove those cards to play.
Efficiency
The number of recursive branches is huge due to the large possible hand of a deck of cards. So the time overhead is very large, for the factorial level O (n!), according to the Stirling formula, about O (n^n).
Because there may be a lot of duplicate cards appearing, there are many recurring recursive calls. So adding a cache can greatly improve efficiency.
That is, the description of our hand and enemy hand and the last hand is the (str(me_pokers)+str(enemy_pokers)+str(last_hand))
key, the results will be obtained into the cache dictionary. The next time you encounter the same situation, you can remove it directly from the cache dictionary without having to repeat the calculation again. Time complexity is optimized to refer to the number of O (c^n).
Results
The result of the code operation is that farmers do not have a winning strategy. In other words, as long as the landlord can play, farmers can't win. is the class solidified so?
Open source
The
Code is put on the github:doudizhu_solver, or you can download it locally, the MIT Protocol, and play it casually.