Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet Artificial Intelligence Lab instructions Mathias Broxvall, Lia Silva-Lopez, November 2011 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet General instructions This text together with some files on the course web page describes the labs in the course Artificial Intelligence, given at Örebro University Autumn 2011. In these labs you will first familiarise yourself with Python, an interpretative, incremental programming language supporting functional and higher order programming. You will use this language to write a number of artificial intelligence type of programs. Platform The labs are primarily intended to be performed under Windows in the University computer halls. However, since Python and the tools used are freely available also for other platforms, you are encouraged to download the language and tools if you want to work from home. If you want to use another platform than the University classrooms you will find some useful information on the course web page, however note that it is your own responsibility of making it work in case you choose alternative platforms. Program code In order to solve the exercises you will use or build upon some existing code. All the files needed to complete the labs can be found at the course web page and you can download it to your computer during the labs. You should also create a file template containing your name, lab group and educational program. This template should be used for all code you present in this course. Preparations In order to benefit properly, you are expected to prepare well before each lab. Remember that this is for your own benefit, and unprepared students may be denied assistance. These preparations include: 1. To have read the exercises and any other material such as lab skeletons, or other source files and instructions. If any of the exercise assignments or questions are unclear you should be able to ask the instructor in the beginning of the session. 2. Go through the lecture notes covering the corresponding area. 3. You should have thought through how the exercises can be solved. This can be done, for instance, by sketching up a rough structure (the design) of your program and to lookup built-in functions in Python that may be used. 4. Considered what can possibly go wrong in your program and how you can fix it. Also make plans for how you can test different parts of your program while writing it, and finally how you can test the whole program. 1 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet Examination For the labs you may work individually or in pairs. Although you are encouraged to discuss different solutions with your classmates you are not allowed to copy code or to show your code to your class mates. All labs should be handed on time lab. Please note that this means that you will have to work outside the scheduled time in order to finish in time, and that you are expected to be able to handle the programming environment and to debug your own code. The examination of each lab consists of two parts: • Part one, is a demonstration of how your code works. This demonstration should be performed to the lab assistant, and will last up to 10 minutes, followed by a questioning round of 5 minutes, where the assistant will ask a number of related questions. You and your team mate for should be present this assignment. If your solution performs as expected, you present it in a correct and concise way, and answer the questions appropriately, you should be allowed to present the second part of the examination. Please note that it is your own responsibility to make a rational use of your available 10 minutes to describe your solution, and present your code to the lab assistant. If your time is due, and you have not finished explaining your solution and answering questions in a satisfying way, or your code does not perform accordingly during this time, the result is a fail, so punctuality and correctness are highly recommended. • Part two, is a printed lab report describing the program and your solution. If you pass part one, you will be allowed to present a report that should describe and motivate the solutions you have used, the intentions behind your solution, and a description of each function you have written as well as its parameters. You should provide an electronic version (in PDF only) of this report. Only those that passed part one of the examination are allowed to present part two. To pass the labs, the following is required: 1. Part one of the lab should be presented before noon of due date. To do so, book a time with the lab assistant, by sending an email with subject ”Oral Part lab xx”, where ”xx” is the number of the lab, and include at least three preferred dates with time intervals. The lab assistant will book your time and update the Calendar with any of your preferred dates. This will be done around 5:30pm of that same day, if the email is received before 5pm. If the email was received after 5pm, times will be assigned the next day. If for some reason none of your preferred dates is feasible, the lab assistant will let you know ASAP. Check the calendar for booking times in http://alturl.com/y52ab. 2. As for part two, labs are corrected within one week and returned at the next scheduled lab occasion. The results of the correction can be either pass, completion or fail. 3. For a lab to be corrected it must be handed in time, after passing part one. Otherwise it will automatically count as a completion. 2 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet 4. If you have received completion on a lab, you must deliver the completed lab report within two weeks. The correction of this ”completed” lab report will be done within a week and will give either pass or fail. Please note that you can not receive a ”complete” more than once!. 5. Please note that there is only one chance to present part one. Use it wisely. 6. Each group should perform, at least once, an oral presentation of the solution to an exercise of one ot the labs. This is assigned from the beginning of the course. The report must satisfy at least the following requirements: • All questions in the exercise must be correctly answered and motivated. Only repeating the question or providing a factually correct yes/no answer does not count as answering it. • The solutions to exercises must be described in a concise and complete way. • The report cannot be longer than 4 pages, unless you have a really good excuse. If you have used any code from other sources than the lab skeletons (eg. Internet, books etc.) you have to clearly mark this in the source code. Not doing so counts as attempted cheating and you will be reported to the disciplinary board. Depending on the amount of borrowed code, the instructor may fail the lab, so always discuss with the instructor before using any code that you have not written yourself. Due to the limited instructor time, all labs must be completed and passed within the allotted time of the course, otherwise you will have to resubmit the labs the following year. Finally, a short clarification of the expected work. Since this is a half-time course you are expected to spend 20 hours/week on this course. This entails roughly 4 hours of lectures and 4 hours of preparations before the lectures, 4 hours of teacher assisted work and 9 hours of preparations for the labs and labs done on your own time. Examiner: Dr. Mathias Broxvall, mathias.broxvall@oru.se Assistant: MSc. Lia Susana d.C. Silva Lopez, lia.silva@oru.se 3 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet 4 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet Lab 1 - An introduction to Python This first lab assignment deals with generic python and functional programming problems. Preparations: Before you start on this lab you should have read the document Artificial Intelligence - A Primer for the Labs and should be familiar with Python. The following is a function that counts the number of times a string occurs in another string: # Count the number of times string s1 is found in string s2 def countsubstring(s1,s2): count = 0 for i in range(0,len(s2)-len(s1)+1): if s1 == s2[i:i+len(s1)]: count += 1 return count For instance, countsubstring(’ab’,’cabalaba’) returns 2. Exercise 1.1 - Recursion. Write a recursive version of the above function. To get the rest of a string (i.e. everything but the first character), you can use s[1:]. Also think about what the computational complexities of the two functions are. Exercise 1.2 - Higher order functions. Write a higher-order function count that counts the number of elements in a list that satisfy a given test. For instance: count(lambda x: x>2, [1,2,3,4,5]) should return 3, as there are three elements in the list larger than 2. Solve this task without using any existing higher-order function. Exercise 1.3 - Brute force solution to the Knapsack problem. Write a function that allows you to generate random problem instances for the knapsack program described in the text Artificial Intelligence - A Primer for the Labs. This function should generate a list of items containing N items that each have a unique name, a random size in the range 1 . . . 5 and a random value in the range 1 . . . 10. Next, you should perform performance measurements to see how long the given knapsack solver take to solve different problem sizes. You should peform atleast 10 runs with different randomly generated problem instances for the problem sizes 10,12,14,16,18,20 and 22. Use a backpack size of 2.5 × N for each value problem size N . Please note that the method used to generate random numbers can also affect performance, since different distributions of values can make the initial conditions of the problem slightly more or less demanding. How much longer time does it take to run this program when we increase the number of items? Does the backpack size affect the answer? Try running the above tests again with a backpack size of 1 × N and with 4.0 × N . Does this affect the performance? 5 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet Give a diagram that shows how the time required increases with problem size for the different backpack sizes. To generate random numbers you should first use import random and can then use random.randrange(start,stop) to generate random numbers in the range start, start+1, ... stop-1. You can measure the time it takes to run the program by using the class Timer from the module timeit. from timeit import * def fn(): ... code to execute ... T=Timer(fn) T.timeit(10) The argument 10 to the function timeit is how many times to execute the function, which makes timing fast functions much easier. Adjust the number so that you don’t have to wait forever for your timing results to finish (this varies for different computers and different python compiler). Hint: Make a python script that runs the code for the different sizes and backsize sizes and that prints only the total execution times. This will simplify this exercise for you. Question 1.1 - Recursive search. What is the worst-case computational complexity of the given knapsack solver? Explain what this means in practical terms! For example: Exercise 1.4 - The Graph colouring problem. Assume that you are organising a party for N people and have been given a list L of people who, for social reasons, should not sit at the same table. Furthermore, assume that you have C tables (that are infinitly large). Write a function layout(N,C,L) that can give a table placement (ie. a number from 0 . . . C − 1) for each guest such that there will be no social mishaps. For simplicity we assume that you have a unique number 0 . . . N − 1 for each guest and that the list of restrictions is of the form [(X,Y), ...] denoting guests X, Y that are not allowed to sit together. Answer with a dictionary mapping each guest into a table assignment, if there are no possible layouts of the guests you should answer False. >>> layout(3,2,[(0,1),(0,2)]) {0: 0, 1: 1, 2: 1} >>> layout(4,2,[(0,1),(0,2),(1,2),(0,3)]) False >>> layout(4,3,[(0,1),(0,2),(1,2),(0,3)]) {0: 0, 1: 1, 2: 2, 3: 1} 6 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet Hint: Your function should be recursive and at a given recursive depth d you should attempt to find a table for guest number d. You can pass d in as an argument and include d in front of each printout you make while debugging your program. On each call to the function, keep track of the guests that have already been assigned to a table and try to decide the placement for the guest d. Do this by considering each possible table t and see if placing him at this table means that he is sitting at the same table as one of his enemies that have already received a table placement. If not, assign (temporarily) guest d to table t and call yourself recursively to find an assignement for guest d + 1. If your recursive call returns successfully you have found a solution, otherwise try to assign d to the next possible table. Hint: Try running your program on very small problem instances first, for instance the simplest case with zero guests or with only one guest. Use both simple examples that possible and examples that are not possible to find a layout for. For example: # Two guests that dislike each other, but only one table >>> layout(2,1,[(0,1)]) False # Same example, but two tables >>> layout(2,2,[(0,1)]) {0: 0, 1: 1} Examination To pass this lab you must first demonstrate each assignment to the assistant (pass part one of the evaluation), and to hand in a detailed report together with the source code, as files sent by email (pass two of the evaluation). You should have one section in your report for each exercise (1.1 - 1.4) and for each question (1.1). Examination date: Latest Nov 17th 7 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet Lab 2 - Search strategies In this second lab assignment you will take a look at search algorithms to perform a few simple tasks. Preparations: Read through the lecture notes regarding search before you start on this lab. You should know the difference between depth-first search, breath-first search and other search methods such as A*. In Figure 1 you will find a matrix (list of lists) that represents a simple labyrinth. 0 represents a wall and 1 represents a passage. 2 represents a door that requires a key to be opened (for future labs) and 3 represents a supply of keys. labyrinth = \ [[0,0,0,0,0,0,1,0], [0,1,0,1,1,1,1,0], [0,1,1,1,0,1,0,0], [0,1,0,0,0,0,0,0], [0,1,1,0,1,1,3,0], [0,0,1,1,1,0,0,0], [0,1,2,0,1,1,1,0], [0,1,0,0,0,0,0,0]] ############. ## ## ##. . . . ## ##. . . ## #### ##. ############ ##. . ##. . .K## ####. . . ###### ##. .D## ## ##. ############ Figure 1: Matrix (left) and printed (right) representation of a labyrinth Exercise 2.1 - Arrays and formatting text. Write a function that takes a labyrinth matrix like the one in Figure 1 as argument and draws it using the characters ## for walls, two spaces for empty passages, D for doors and K for keys. This function should also accept a list of (x,y) tuples that represent positions visited by a robot in the labyrinth, you should print the character . at these points (note that you should print either a whitespace, D or K in addition to the dot). You can use either the print statement and the formatting instructions to do this, or to use sys.stdout.write to write strings without appended newlines. For the later approach remember that you need to first use import sys in your code. When you access the matrix, remember that the y axis (row) comes before the x axis (column): labyrinth[y][x]. Exercise 2.2 - Identifying free areas of the labyrinth. Write a function adjacent_passages that takes a labyrinth matrix like the one above and the x and y coordinates for a place in the labyrinth as arguments, and returns a list of the coordinates (tuples of x and y) of all adjacent places (vertically and horizontally) that are passages (i.e. have value 1). For instance: >>> adjacent_passages(labyrinth, 1, 1) [(1, 2)] >>> adjacent_passages(labyrinth, 6, 1) 8 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet [(6, 0), (5,1)] Note that the function must handle all input coordinates in a consistent way, and not access memory outside the size of the labyrinth array under any circumstance. Hint: Test that your function gives the correct answer and does not crash when tested on the corner poins (0,0), (7,0), (0,7), (7,7) as well as the opening points (6,0), (7,1) and completly invalid points such as (42,11), (-1,117) Now remains to find a path through the labyrinth from the previous exercise. Such a path can be described as a list of (x,y)-coordinates. As is common in many 2D computer games (and reality) we usually need to hold a key before we can open a door. We also consume the keys when opening doors (unlike reality) but can fortunatly find an infinite supply of keys just lying around on the floor. Your next exercise it to take a path (list of (x,y) coordinates) and verify that the user holds picks up a key before attempting to move through any doors. Exercise 2.3 - Verifying a valid path. Write a function that can take a list [(x1,y1),(x2,y2),...] containing the coordinates of a walk through the labyrinth and that can verify that a key is picked up before attempting to walk through any doors. Assume that the user can only hold one key, and that he automatically picks up the key when walking over any coordinate containing keys (the keys in the labyrinth are not consumed by this). If the path attempts to go through a door without holding a key then your function should return false, otherwise it should continue checking the path but without holding a key. Hint: Test your function on the labyrinth given above. It should not be possible to exist the labyrinth with a straight path, but it should be possible if we first go over the key as showned in the example printout. Question 2.1 - Loop detection. What is loop detection and why is this necessary when searching through the labyrinth? How can you implement this in an efficient way? Does it matter if the player comes back to the same place twice, but possibly with or without holding a key? Next, it is time for you to do some more serious adventuring through labyrinths. Start by downloading labyrinth.py containing a slightly larger labyrinth for these exercise. Exercise 2.4 - Searching through the labyrinth. Write a recursive function that finds a way through the labyrinth. The entrance is at (6,0) the exit at (1,15) for the big labyrinth. The function should effectively perform a depth-first search. The base case is obviously that the goal is reached, and should return the path leading there. Therefore, you need to keep track of the path traversed so far. In the recursive case, you need to try all possible ways you can take the next step, except for those cases when you return to a state previously visited. 9 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet Hint: A state needs to contain both the coordinate of the player as well as wheter or not he holds a key (true/false). Exercise 2.5 - A generic search method. Recall the search algorithm find_path_df from the text Artificial Intelligence - A Primer for the Labs. One of the advantages of this method is that you have a generic search algorithm, and only need to plugin the methods for knowing which actions you can take and detecting if you have reached the goal or not. Your task now is to complete this algorithm to use to perform the search through the labyrinth. Compare the result with the recursive approach from previous exercise. Exercise 2.6 - A better search. Write a function find_path_bf that implements a breadthfirst search using a queue. You will only need to change a few lines of the previous algorithm for this. Test the algorithm on the labyrinth we have used before. Compare the number of visited nodes and the quality of the solution with the previous approaches. What is the difference? Can you explain the advantages and disadvantages of this approach? Question 2.2 - Comparing the search methods. Compute how many nodes where visited during the depth respectively breadth first search. Which method was faster and which method yielded the shortest path? Also compute/estimate the maximum amount of memory used by the different version of the algorithms. Investigate the different order in which the neighbors of a position is returned by your function adjacent_passages. How does this affect the cost and quality of the solutions generated by your search algorithms above? Exercise 2.7 - A* search. Finally, you should write a new function that instead performs an A* search through the labyrinth. As a path-cost function you should use the length of the current path, and as a distance estimate use the Manhattan distance to the goal. Compare the cost (in terms of visited nodes and maximum memory usage) of finding the solution to the labyrinth using this algorithm compared to the two other search algorithms above. Does your algorithm generate an optimal solution? Question 2.3 - Admissible heuristics. What is an admissible heuristic? Why is this important for A*? Is the Manhattan distance a valid heuristic for the problem above? If you would have used instead a heurstic “key-greedy” that have as cost the manhattan distance plus ten if we do not yet hold a key. Would this “key-greedy” heuristic be admissible? Question 2.4 - Comparing the search algorithms. Explain under what circumstances the three 10 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet different search algorithms you have tested are “best”. When should each one be used in favor of the others? Examination To pass this lab you must first demonstrate each assignment to the assistant (pass part one of the evaluation), and to hand in a detailed report together with the source code, as files sent by email (pass two of the evaluation). You should have one section in your report for each exercise (Exercise 2.1 - 2.7) and for each question (Question 2.1 - 2.4). Examination date: Latest Dec 1st 11 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet Lab 3 - A game playing AI It is now time for you to develop your first full game AI, for the board game Reversi. This game is also sometimes known as Othello. It is a two player game where the two players, black and white, alternate in placing the black respectively white side of a flat disk in one of the 8x8 positions of the game board. When they place a disk on a position, they get to flip over all of the opponents disks in a straight line (including diagonal lines) between this position and one of the players already placed markers. It is only allowed to make moves that will flip over at least one of the other players disk, and the game ends when one player can no longer make any more moves. See Figure 2 for a picture of this game. Start by downloading reversi.zip from the course web page. Unpack this archive and you will find a Python file (reversi.py) and a number of image files (GIF) that are used to generate a visualization of your program. If you are working from home, you may need to install tkinter, which is the de-facto standard python GUI toolkit1 . Figure 2: The game Reversi, aka. Othello Test run the game by loading reversi.py. You will now see the board and can play as human versus a very bad AI. The AI that you have been given in this code skeleton randomly chooses one of the possible moves that are legal from each position. Question 3.1 - Loop detection. What is the maximum number of moves that can be done by each player in this game? Do you need to handle loop detection when searching for the best moves? Motivate your answer! You can change which player(s) are human, and which are run by which AI by passing an instance of the classes to the function ReversiFrame(..,..). The two arguments correspond 1 Under Ubuntu, install python-tk for this 12 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet to each player, and if they are None it means that the player is human controlled, otherwise it should be an instance of a class that implements the doMove function. Exercise 3.1 - A greedy search algorithm. Start by implementing a greedy AI algorithm that goes through each move it can do in the current state, and performs the move that would give the highest score immediately. You should do this implementation by creating a new class, that implements the doMove function to perform the best move. If there are multiple moves that gives the same score, one of them should be choosen randomly. Test to play against this AI, is it harder to win against this than the random AI? Also, test to run your AI against the random AI at least 10 times and record which player was the winner. How often does your AI win against the random AI? You can use the given function verifyAIs for this. If you read up on strategies for Reversi, you will find that beginners usally only play to flip the most disks each move. This corresponds to your greedy AI above. Slightly more advanced players learn to look ahead a few moves, to avoid giving the other player “free chances” to flip many of your pieces. Exercise 3.2 - The min-max algorithm. Next, you should implement a smarter AI that employs the min-max algorithm to rank the score of each possible move that it can do. It should have a look-ahead of atleast 3 half-moves (ply’s) and should run fast enough to be playable without waiting “too long”. In case of multiple moves giving the same score a random choise should be made. Again, this AI should be implemented in a new class. Test to play against this AI, is it harder to win against this than the previous two AI’s? Test to run your AI against both the random AI and the greedy AI atleast 10 times and record which player was the winner. How often does your AI win against these two other AI’s. Finally, if you have played Reversi alot, you have probably learned that certain positions on the game board is better than the others (otherwise, you can read about strategies for this game online). You should use this knowledge to develop a smarter heuristic, that makes the AI value certain positions more and try to win these positions. Exercise 3.3 - Adding a heuristic. You should make your min-max AI slightly more sophisticated by employing a heuristic that rewards certain positions. Try a few different heuristics and compare them against each other and against the previously developed AI’s. Finally, test to play against your AI and make test runs to statistically determine which AI and heuristic is the best. Examination This lab may be performed in groups of two or individually. To pass this lab you must first demonstrate each assignment to the assistant (pass part one of the evaluation), and to hand in a detailed report together with the source code, as files sent by email (pass two of the evaluation). 13 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet You should have one section in your report for each exercise (Exercise 3.1 - 3.3) and for each question (Question 3.1). Examination date: Latest Dec 15th 14 Artificial Intelligence - Lab Instructions v2011.1, Örebro Universitet PRELIMINARY Lab 4 - Neural Networks This lab will be written and published on the course webpage before Dec 8th. 15