Thursday, February 2, 2023
Learning Code
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#
No Result
View All Result
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#
No Result
View All Result
Learning Code
No Result
View All Result
Home Python

Learn How to Lose the Game of Nim – Real Python

learningcode_x1mckf by learningcode_x1mckf
October 5, 2022
in Python
0
Learn How to Lose the Game of Nim – Real Python
74
SHARES
1.2k
VIEWS
Share on FacebookShare on Twitter


You’ve gotten to know the steps of the minimax algorithm. On this part, you’ll implement minimax in Python. You’ll begin by tailoring the algorithm on to the sport of Easy-Nim. Later, you’ll refactor your code to separate the core of the algorithm from the principles of the sport, such you could later apply your minimax code to different video games.

Implement a Nim-Particular Minimax Algorithm

Contemplate the identical instance as within the earlier part: it’s Maximillian’s flip, and there are six counters on the desk. You’ll use the minimax algorithm to verify that Maximillan can win this sport and to calculate his subsequent transfer.

First, although, take into account a couple of examples of late-game conditions. Assume it’s Maximillian’s flip and he sees the next sport state of affairs:

  • Zero counters signifies that Mindy has already taken the final counter. Maximillian wins the sport.
  • One counter doesn’t go away any selection for Maximillian. He takes the counter, which leaves zero counters for Mindy. Utilizing the identical logic as within the earlier bullet level however with the gamers’ roles reversed, you see that Mindy wins the sport.
  • Two counters offers Maximillian a selection. He can take one or two counters, which can go away Mindy with one or zero counters, respectively. Redo the logic from the earlier bullet factors from Mindy’s viewpoint. You’ll word that if Maximillian takes one counter, he leaves one counter and can win the sport. If he takes two counters, he leaves zero counters, and Mindy wins the sport.

Observe the way you reuse logic from earlier bullet factors to determine who can win from a selected sport place. Now begin to implement the logic in Python. Create a file named minimax_simplenim.py. You’ll use state to symbolize the variety of counters and max_turn to maintain monitor of whether or not it’s Maximillian’s flip or not.

The primary rule, for zero counters, may be applied as a conditional if test. You come back 1 if Maximillian wins the sport and -1 if he loses:

# minimax_simplenim.py

def minimax(state, max_turn):
    if state == 0:
        return 1 if max_turn else -1

    # ...

Subsequent, take into consideration the way to take care of sport conditions with a number of counters. They cut back to a number of states with fewer counters, the place the alternative participant strikes first. For instance, if it’s at the moment Maximillian’s flip, take into account the outcomes of his attainable strikes and select the perfect one:

# minimax_simplenim.py

def minimax(state, max_turn):
    if state == 0:
        return 1 if max_turn else -1

    possible_new_states = [
        state - take for take in (1, 2, 3) if take <= state
    ]
    if max_turn:
        scores = [
            minimax(new_state, max_turn=False)
            for new_state in possible_new_states
        ]
        return max(scores)

    # ...

You’ll first enumerate the attainable new states, ensuring that the gamers gained’t take extra counters than can be found. You calculate the scores of Maximillian’s attainable strikes by calling minimax() once more, noting that it’ll be Mindy’s flip subsequent. As a result of Maximillian is the maximizing participant, you’ll return the utmost of his attainable scores.

Equally, if it’s at the moment Mindy’s flip, take into account her choices. Since -1 signifies that she’s successful, she’ll choose the end result with the smallest rating:

# minimax_simplenim.py

def minimax(state, max_turn):
    if state == 0:
        return 1 if max_turn else -1

    possible_new_states = [
        state - take for take in (1, 2, 3) if take <= state
    ]
    if max_turn:
        scores = [
            minimax(new_state, max_turn=False)
            for new_state in possible_new_states
        ]
        return max(scores)
    else:
        scores = [
            minimax(new_state, max_turn=True)
            for new_state in possible_new_states
        ]
        return min(scores)

The minimax() perform repeatedly calls itself till it reaches the tip of every sport. In different phrases, minimax() is a recursive perform.

Observe: Implementing a recursive perform is an intuitive technique to traverse timber as a result of exploring a department of a tree is identical operation as exploring the larger tree.

Nonetheless, recursive features have some issues, specifically for greater timber. In Python, perform calls have some overhead, and the call stack is restricted.

You’ll see the way to counter a few of these points later. However a non-recursive implementation of minimax could also be a greater possibility if you must optimize the minimax algorithm for pace.

First, verify that minimax() works as anticipated. Open a Python REPL and import your perform:

>>>

>>> from minimax_simplenim import minimax
>>> minimax(6, max_turn=True)
1

>>> minimax(5, max_turn=False)
1

>>> minimax(4, max_turn=False)
-1

You first verify that Maximillian can win if he performs with six counters left, as represented by 1. Equally, Maximillian can nonetheless win if he leaves 5 counters for Mindy. If as a substitute he leaves 4 counters for Mindy, then she is going to win.

To successfully discover which transfer Maximillian ought to play subsequent, you are able to do the identical calculations in a loop:

>>>

>>> state = 6
>>> for take in (1, 2, 3):
...     new_state = state - take
...     rating = minimax(new_state, max_turn=False)
...     print(f"Transfer from state to new_state: rating")
...
Transfer from 6 to five: 1
Transfer from 6 to 4: -1
Transfer from 6 to three: -1

On the lookout for the very best rating, you see that Maximillian ought to take one counter and go away 5 on the desk. Subsequent, take this one step additional and create a perform that may discover Maximillian’s greatest transfer:

# minimax_simplenim.py

# ...

def best_move(state):
    for take in (1, 2, 3):
        new_state = state - take
        rating = minimax(new_state, max_turn=False)
        if rating > 0:
            break
    return rating, new_state

You loop till you discover a transfer that provides a constructive rating—in impact, a rating of 1. You may additionally loop by all three attainable strikes with out discovering a successful transfer. To point this, you come each the rating and the perfect transfer:

>>>

>>> best_move(6)
(1, 5)

>>> best_move(5)
(-1, 2)

Testing your perform, you verify that when confronted with six counters, Maximillian can win the sport by eradicating one counter and leaving 5 for Mindy. If there are 5 counters on the desk, all strikes have a rating of -1. Despite the fact that all strikes are equally unhealthy, best_move() suggests the final transfer that it checked: take three counters and go away two.

Look again at your code for minimax() and best_move(). Each features comprise logic that offers with the minimax algorithm and logic that offers with the principles of Easy-Nim. Within the subsequent subsection, you’ll see how one can separate these.

Refactor to a Basic Minimax Algorithm

You’ve coded the next guidelines of Easy-Nim into your minimax algorithm:

  • A participant can take one, two, or three counters on their flip.
  • A participant can’t take extra counters than are left within the sport.
  • The sport is over when there are zero counters left.
  • The participant who takes the final counter loses the sport.

Moreover, you’ve used max_turn to maintain monitor of whether or not Maximillian is the lively participant or not. To be extra common, you’ll be able to suppose of the present participant because the one who needs to maximise their rating. To point this, you’ll substitute the max_turn flag with is_maximizing.

Begin rewriting your code by including two new features:

# minimax_simplenim.py

# ...

def possible_new_states(state):
    return [state - take for take in (1, 2, 3) if take <= state]

def consider(state, is_maximizing):
    if state == 0:
        return 1 if is_maximizing else -1

These two features implement the principles of Easy-Nim. With possible_new_states(), you calculate the attainable subsequent states whereas ensuring {that a} participant can’t take extra counters than these out there on the board.

You consider a sport place with consider(). If there aren’t any counters left, then the perform returns 1 if the maximizing participant gained the sport and -1 if the opposite—minimizing—participant gained. If the sport isn’t over, execution will proceed to the tip of the perform and implicitly return None.

Now you can rewrite minimax() to confer with possible_new_states() and consider():

# minimax_simplenim.py

def minimax(state, is_maximizing):
    if (rating := consider(state, is_maximizing)) is not None:
        return rating

    if is_maximizing:
        scores = [
            minimax(new_state, is_maximizing=False)
            for new_state in possible_new_states(state)
        ]
        return max(scores)
    else:
        scores = [
            minimax(new_state, is_maximizing=True)
            for new_state in possible_new_states(state)
        ]
        return min(scores)

Keep in mind to additionally rename max_turn to is_maximizing.

You solely rating a sport state if there are zero counters left and the winner has been determined. Subsequently, you must verify whether or not rating is None to resolve if you happen to ought to proceed to name minimax() or return the sport analysis. You utilize an assignment expression (:=) to each verify and bear in mind the evaluated sport rating.

Subsequent, observe that the blocks in your if … else assertion are fairly comparable. The one variations between the blocks are which perform, max() or min(), you employ to search out the perfect rating and what worth you employ for is_maximizing within the recursive calls to minimax(). Each of those may be straight calculated from the present worth of is_maximizing.

You possibly can due to this fact collapse the if … else block into one assertion:

# minimax_simplenim.py

def minimax(state, is_maximizing):
    if (rating := consider(state, is_maximizing)) is not None:
        return rating

    return (max if is_maximizing else min)(
        minimax(new_state, is_maximizing=not is_maximizing)
        for new_state in possible_new_states(state)
    )

You utilize a conditional expression to name both max() or min(). To reverse the worth of is_maximizing, you go not is_maximizing to the recursive calls to minimax().

The code for minimax() is now fairly compact. Extra importantly, the principles of Easy-Nim aren’t explicitly encoded throughout the algorithm. As a substitute, they’re encapsulated in possible_new_states() and consider().

You end the refactoring by additionally expressing best_move() when it comes to possible_new_states() and with is_maximizing as a substitute of max_turn:

# minimax_simplenim.py

# ...

def best_move(state):
    for new_state in possible_new_states(state):
        rating = minimax(new_state, is_maximizing=False)
        if rating > 0:
            break
    return rating, new_state

As earlier than, you verify the end result of every attainable transfer and return the primary that ensures a win.

Observe: There’s no error dealing with in best_move(). Particularly, it assumes that possible_new_states() returns at the least one new sport state. If it doesn’t, then the loop gained’t run in any respect, and rating and new_state might be undefined.

Which means that it’s best to solely name best_move() with a legitimate sport state. Alternatively, you’ll be able to add an additional verify inside best_move() itself.

In Python, tuples are in contrast aspect by aspect. You possibly can benefit from this to make use of max() straight as a substitute of checking the attainable strikes explicitly:

# minimax_simplenim.py

# ...

def best_move(state):
    return max(
        (minimax(new_state, is_maximizing=False), new_state)
        for new_state in possible_new_states(state)
    )

As earlier, you take into account—and return—a tuple containing the rating and the perfect new state. As a result of comparisons, together with max(), are finished aspect by aspect in tuples, the rating have to be the primary aspect within the tuple.

You might also like

Build a JavaScript Front End for a Flask API – Real Python

Using the Terminal on Linux – Real Python

How to Iterate Over Rows in pandas, and Why You Shouldn’t – Real Python

You’re nonetheless capable of finding the optimum strikes:

>>>

>>> best_move(6)
(1, 5)

>>> best_move(5)
(-1, 4)

As earlier than, best_move() means that it’s best to take one counter if you happen to’re confronted with six. Within the shedding state of affairs of 5 counters, it doesn’t matter what number of counters you’re taking, since you’re about to lose anyway. Your implementation based mostly on max() finally ends up leaving as many counters on the desk as attainable.

You possibly can develop the field under to see the total supply code that you just’ve applied on this part:

You’ve encapsulated the principles of Easy-Nim in possible_new_states() and consider(). These features are utilized by minimax() and best_move():

# minimax_simplenim.py

def minimax(state, is_maximizing):
    if (rating := consider(state, is_maximizing)) is not None:
        return rating

    return (max if is_maximizing else min)(
        minimax(new_state, is_maximizing=not is_maximizing)
        for new_state in possible_new_states(state)
    )

def best_move(state):
    return max(
        (minimax(new_state, is_maximizing=False), new_state)
        for new_state in possible_new_states(state)
    )

def possible_new_states(state):
    return [state - take for take in (1, 2, 3) if take <= state]

def consider(state, is_maximizing):
    if state == 0:
        return 1 if is_maximizing else -1

Use best_move() to search out the subsequent transfer in a given sport.

Nice job! You’ve applied a minimax algorithm for Easy-Nim. To problem it, it’s best to play a couple of video games in opposition to your code. Begin with some variety of counters and take turns eradicating counters your self and utilizing best_move() to decide on what number of counters your digital opponent will take away. Except you play an ideal sport, you’ll lose!

Within the subsequent part, you’ll implement the identical algorithm for the common guidelines of Nim.





Source link

Share30Tweet19
learningcode_x1mckf

learningcode_x1mckf

Recommended For You

Build a JavaScript Front End for a Flask API – Real Python

by learningcode_x1mckf
February 1, 2023
0
Build a JavaScript Front End for a Flask API – Real Python

Most fashionable net functions are powered by a REST API below the hood. That manner, builders can separate JavaScript front-end code from the back-end logic that an online...

Read more

Using the Terminal on Linux – Real Python

by learningcode_x1mckf
January 31, 2023
0
Using the Terminal on Linux – Real Python

The terminal might be intimidating to work with once you’re used to working with graphical consumer interfaces. Nonetheless, it’s an vital device that you have to get used...

Read more

How to Iterate Over Rows in pandas, and Why You Shouldn’t – Real Python

by learningcode_x1mckf
January 30, 2023
0
How to Iterate Over Rows in pandas, and Why You Shouldn’t – Real Python

One of the crucial frequent questions you may need when coming into the world of pandas is easy methods to iterate over rows in a pandas DataFrame. In...

Read more

Orchestrating Large and Small Projects With Apache Airflow – The Real Python Podcast

by learningcode_x1mckf
January 27, 2023
0
Orchestrating Large and Small Projects With Apache Airflow – The Real Python Podcast

Jan 27, 2023 54m Have you ever labored on a mission that wanted an orchestration device? How do you outline the workflow of a complete information pipeline or...

Read more

Try Out Code and Ideas Quickly – Real Python

by learningcode_x1mckf
January 25, 2023
0
Try Out Code and Ideas Quickly – Real Python

The Python customary shell, or REPL (Learn-Eval-Print Loop), lets you run Python code interactively whereas engaged on a mission or studying the language. This instrument is on the...

Read more
Next Post
Java Developer (Hybrid JHB/PTA) at Datafin Recruitment

Java Developer (Hybrid JHB/PTA) at Datafin Recruitment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Related News

Java Web Frameworks Software Market Overview with Detailed Analysis, Competitive landscape, Forecast to 2028 – Panama Travel News

Java Web Frameworks Software Market Overview with Detailed Analysis, Competitive landscape, Forecast to 2028 – Panama Travel News

December 12, 2022
What Is Java? (Definition, Uses, Popularity)

What Is Java? (Definition, Uses, Popularity)

January 23, 2023
Senior Java Developer – IT-Online

Senior Java Developer – IT-Online

November 17, 2022

Browse by Category

  • C#
  • C++
  • Java
  • JavaScript
  • Python
  • Swift

RECENT POSTS

  • Java :Full Stack Developer – Western Cape saon_careerjunctionza_state
  • Pay What You Want for this Learn to Code JavaScript Certification Bundle
  • UPB Java Jam brings coffeehouse vibes to Taylor Down Under | Culture

CATEGORIES

  • C#
  • C++
  • Java
  • JavaScript
  • Python
  • Swift

© 2022 Copyright Learning Code

No Result
View All Result
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#

© 2022 Copyright Learning Code

Are you sure want to unlock this post?
Unlock left : 0
Are you sure want to cancel subscription?