<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Introduction
In the tutorial, you learned how to build a reasonably intelligent agent with the minimax algorithm.  In this exercise, you will check your understanding and submit your own agent to the competition.
Step1:  1) A closer look
The heuristic from the tutorial looks at all groups of four adjacent grid locations on the same row, column, or diagonal and assigns points for each occurrence of the following patterns
Step2:  2) Count the leaves
In the tutorial, we worked with a small game tree.
<center>
<img src="https
Step3:  3) Which move will the agent select?
In this question, you'll check your understanding of the minimax algorithm.  Remember that with this algorithm, 
The agent chooses moves to get a score that is as high as possible, and it assumes the opponent will counteract this by choosing moves to force the score to be as low as possible.
Consider the toy example below of a game tree that the agent will use to select its next move.
<center>
<img src="https
Step4:  4) Examine the assumptions
The minimax agent assumes that its opponent plays optimally (with respect to the heuristic, and using a game tree of limited depth).  But this is almost never the case, in practice
Step5:  5) Submit to the competition
Now, it's time to submit an agent to the competition!  Use the next code cell to define an agent.  (You can see an example of how to write a valid agent in this notebook.)
If you decide to use the minimax code from the tutorial, you might like to add alpha-beta pruning to decrease the computation time (i.e., get the minimax algorithm to run much faster!).  In this case, "alpha" and "beta" to refer to two values that are maintained while the algorithm is running, that help to identify early stopping conditions.  
Without alpha-beta pruning, minimax evaluates each leaf node.  With alpha-beta pruning, minimax only evaluates nodes that could provide information that affects the agent's choice of action.  Put another way, it identifies nodes that could not possibly affect the final result and avoids evaluating them.
<ASSISTANT_TASK:>
Python Code:
from learntools.core import binder
binder.bind(globals())
from learntools.game_ai.ex3 import *
Explanation: Introduction
In the tutorial, you learned how to build a reasonably intelligent agent with the minimax algorithm.  In this exercise, you will check your understanding and submit your own agent to the competition.
End of explanation
#_COMMENT_IF(PROD)_
q_1.hint()
# Check your answer (Run this code cell to receive credit!)
q_1.solution()
Explanation: 1) A closer look
The heuristic from the tutorial looks at all groups of four adjacent grid locations on the same row, column, or diagonal and assigns points for each occurrence of the following patterns:
<center>
<img src="https://i.imgur.com/3NvBEGL.png" width=70%><br/>
</center>
Is it really necessary to use so many numbers to define the heuristic?  Consider simplifying it, as in the image below.
<center>
<img src="https://i.imgur.com/grViegG.png" width=70%><br/>
</center>
How would each heuristic score the potential moves in the example below (where, in this case, the agent looks only one step ahead)?  Which heuristic would lead to the agent selecting the better move?
<center>
<img src="https://i.imgur.com/LWPLy7N.png" width=100%><br/>
</center>
End of explanation
# Fill in the blank
num_leaves = ____
# Check your answer
q_2.check()
#%%RM_IF(PROD)%%
num_leaves = 7*7*7
q_2.check()
# Lines below will give you a hint or solution code
#_COMMENT_IF(PROD)_
q_2.hint()
#_COMMENT_IF(PROD)_
q_2.solution()
Explanation: 2) Count the leaves
In the tutorial, we worked with a small game tree.
<center>
<img src="https://i.imgur.com/BrRe7Bu.png" width=90%><br/>
</center>
The game tree above has 8 leaf nodes that appear at the bottom of the tree.  By definition, "leaf nodes" in a game tree are nodes that don't have nodes below them.
In the ConnectX competition, the game trees will be much larger!  
To see this, consider a minimax agent that is trying to plan its first move, where all columns in the game board are  empty.  Say the agent builds a game tree of depth 3.  How many leaf nodes are in the game tree?  
Use your answer to fill in the blank below.
End of explanation
# Fill in the blank
selected_move = ____
# Check your answer
q_3.check()
#%%RM_IF(PROD)%%
selected_move = 'three'
q_3.assert_check_failed()
#%%RM_IF(PROD)%%
selected_move = 1
q_3.assert_check_failed()
#%%RM_IF(PROD)%%
selected_move = 33
q_3.assert_check_failed()
#%%RM_IF(PROD)%%
selected_move = 3
q_3.assert_check_passed()
# Lines below will give you a hint or solution code
#_COMMENT_IF(PROD)_
q_3.hint()
#_COMMENT_IF(PROD)_
q_3.solution()
Explanation: 3) Which move will the agent select?
In this question, you'll check your understanding of the minimax algorithm.  Remember that with this algorithm, 
The agent chooses moves to get a score that is as high as possible, and it assumes the opponent will counteract this by choosing moves to force the score to be as low as possible.
Consider the toy example below of a game tree that the agent will use to select its next move.
<center>
<img src="https://i.imgur.com/QlfWGM9.png" width=80%><br/>
</center>
Which move will the agent select?  Use your answer to set the value of the selected_move variable below.  Your answer should be one of 1, 2, or 3.
End of explanation
#_COMMENT_IF(PROD)_
q_4.hint()
# Check your answer (Run this code cell to receive credit!)
q_4.solution()
Explanation: 4) Examine the assumptions
The minimax agent assumes that its opponent plays optimally (with respect to the heuristic, and using a game tree of limited depth).  But this is almost never the case, in practice: it's far more likely for the agent to encounter a suboptimal (that is: worse than optimal) opponent.  
Say the minimax agent encounters a suboptimal opponent. Should we expect the minimax agent to still play the game well, despite the contradiction with its assumptions?  If so, why?
End of explanation
def my_agent(obs, config):
    # Your code here: Amend the agent!
    import random
    valid_moves = [col for col in range(config.columns) if obs.board[col] == 0]
    return random.choice(valid_moves)
# Run this code cell to get credit for creating an agent
q_5.check()
import inspect
import os
def write_agent_to_file(function, file):
    with open(file, "a" if os.path.exists(file) else "w") as f:
        f.write(inspect.getsource(function))
        print(function, "written to", file)
write_agent_to_file(my_agent, "submission.py")
Explanation: 5) Submit to the competition
Now, it's time to submit an agent to the competition!  Use the next code cell to define an agent.  (You can see an example of how to write a valid agent in this notebook.)
If you decide to use the minimax code from the tutorial, you might like to add alpha-beta pruning to decrease the computation time (i.e., get the minimax algorithm to run much faster!).  In this case, "alpha" and "beta" to refer to two values that are maintained while the algorithm is running, that help to identify early stopping conditions.  
Without alpha-beta pruning, minimax evaluates each leaf node.  With alpha-beta pruning, minimax only evaluates nodes that could provide information that affects the agent's choice of action.  Put another way, it identifies nodes that could not possibly affect the final result and avoids evaluating them.
End of explanation
<END_TASK>