






Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Information on implementing game search and evaluation functions for the connect four game using python. It includes details on the game rules, board representation, and methods for making moves and checking game status. The document also explains how to write minimax and alpha-beta search algorithms and provides hints for implementing static evaluation functions.
Typology: Exercises
1 / 10
This page cannot be seen from the preview
Don't miss anything!







To work on this problem set, you will need to get the code, much like you did for earlier problem sets.
Your answers for the problem set belong in the main file lab3.py.
This problem set is about game search, and it will focus on the game "Connect Four". This game has been around for a very long time, though it has been known by different names; it was most recently released commercially by Milton Bradley.
A board is a 7x6 grid of possible positions. The board is arranged vertically: 7 columns, 6 cells per column, as follows:
0 1 2 3 4 5 6 0 * * * * * * * 1 * * * * * * * 2 * * * * * * * 3 * * * * * * * 4 * * * * * * * 5 * * * * * * *
Two players take turns alternately adding tokens to the board. Tokens can be added to any column that is not full (ie., does not already contain 6 tokens). When a token is added, it immediately falls to the lowest unoccupied cell in the column.
The game is won by the first player to have four tokens lined up in a row, either vertically:
0 1 2 3 4 5 6 0 1 2 O 3 O * 4 O * 5 O *
horizontally:
0 1 2 3 4 5 6 0 1 2 3
1 2 3 4 5
4 5 * * * O O O O
or along a diagonal:
0 1 2 3 4 5 6 0 1 2 O 3 O O * 4 O O * * 5 O O * * *
0 1 2 3 4 5 6 0 1 2 * 3 O * O O 4 O O * * 5 * O O * *
Playing the game
You can get a feel for how the game works by playing it against the computer. For example, by uncommenting this line in lab3.py, you can play white, while a computer player that does minimax search to depth 4 plays black.
run_game(basic_player, human_player)
For each move, the program will prompt you to make a choice, by choosing what column to add a token to.
The prompt may look like this:
Player 1 (☺) puts a token in column 0
0 1 2 3 4 5 6 0
☺
Pick a column #: -->
4 5 O
>>> myBoard # Just to show that the original board hasn't changed
0 1 2 3 4 5 6 0 1 2 3 4 5
>>>
There are quite a few methods on the ConnectFourBoard object. You are welcome to call any of them. However, many of them are helpers or are used by our tester or support code; we only expect you to need some of the following methods:
Note also that, because ConnectFourBoards are immutable, they can be used as dictionary keys and they can be inserted into Python set() objects.
Other Useful Functions
There are a number of other useful functions in this lab, that are not members of a class. They include the following:
Writing your Search Algorithm
"evaluate" Functions
In this lab, you will implement two evaluation functions for the minimax and alpha-beta searches: focused_evaluate, and better_evaluate. Evaluate functions take one argument, an instance of ConnectFourBoard. They return an integer that indicates how favorable the board is to the current player.
The intent of focus_evaluate is to simply get your feet wet in the wild-and-crazy world of evaluation functions. So the function is really meant to be very simple. You just want to make your player win more quickly, or lose more slowly.
The intent of better_evaluate is to go beyond simple static evaluation functions, and getting your function to beat the default evaluation function: basic_evaluate. There are multiple ways to do this but the most-common solutions involve knowing how far you are into the game at any given time. Also note that, each turn, one token is added to the board. There are a few functions
def my_player(board): return minimax(board, depth=3, eval_fn=focused_evaluate, timeout=5)
or, more succinctly (but equivalently):
my_player = lambda board: minimax(board, depth=3, eval_fn=focused_evaluate)
However, this assumes you want to evaluate only to a specific depth. In class, we discussed the concept of iterative deepening. We have provided the run_search_function helper function to create a player that does iterative deepening, based on a generic search function. You can create an iterative-deepening player as follows:
my_player = lambda board: run_search_function(board, search_fn=minimax, eval_fn=focused_evaluate)
You may notice, when playing against the computer, that it seems to make plainly "stupid" moves. If you gain an advantage against the computer so that you are certain to win if you make the right moves, the computer may just roll over and let you win. Or, if the computer is certain to win, it may seem to "toy" with you by making irrelevant moves that don't change the outcome.
This isn't "stupid" from the point of view of a minimax search. If all moves lead to the same outcome, why does it matter which move the computer makes?
This isn't how people generally play games, though. People want to win as quickly as possible when they can win, and lose slowly so that their opponent has several opportunities to mess up. A small change to the basic player's static evaluation function will make the computer play this way too.
It will help to follow these guidelines:
The computerized players you've used so far would fit in well in the British Museum - they're evaluating all the positions down to a certain depth, even the useless ones. You can make them search much more efficiently by using alpha-beta search.
It may help to read Chapter 6 of the text if you're unclear on the details of alpha-beta search.
This procedure is called by the player alphabeta_player, defined in lab3.py.
Hints
In class, we describe alpha-beta in terms of one player maximizing a value and the other person minimizing it. However, it will probably be easiest to write your code so that each player is trying to maximize the value from their own point of view. Whenever they look forward a step to the other player's move, they negate the resulting value to get a value for their own point of view. This is how the minimax function we provide works.
You will need to keep track of your range for alpha-beta search carefully, because you need to negate this range as well when you propagate it down the tree. If you have determined that valid scores for a move must be in the range [3, 5] -- in other words, alpha=3 and beta=5 -- then valid scores for the other player will be in the range [-5, -3]. So if you use this negation trick, you'll need to propagate alpha and beta like this in your recursive step:
newalpha = -beta newbeta = -alpha
This is more compact than the representation we were using on the board in lecture, and it captures the insight that the player evaluates its opponent's choices just as if the player was making those choices itself.
Ask a TA if you are confused by this.
A common pitfall is to allow the search to go beyond the end of the game. So be sure you check whether someone has won!
This problem is going to be a bit different. There's no single right way to do it - it will take a bit of creativity and thought about the game.
TIP: To save time, change the if True to if False on line 308 of tests.py. Disabling this test will skip the game test that usually takes a few minutes to finish. But be sure to remember to turn it back on when you have passed all your other offline tests!
if True: make_test(type = 'MULTIFUNCTION', getargs = run_test_game_1_getargs, testanswer = run_test_game_1_testanswer, expected_val = "You must win at least 2 more games than you lose to pass this test", name = 'run_test_game' )
Please answer these questions at the bottom of your lab3.py file:
(We'd ask which parts you find confusing, but if you're confused you should really ask a TA.)
When you're done, run the tester as usual to submit your code.