Вы находитесь на странице: 1из 66

CMSC 15100: Lab 1

Tuesday 10/1, Wednesday 10/2


CS151 Coursework | CS151 Home Lab 1 is a self-guided set of exercises intended to increase your familiarity with the command line, subversion, and DrRacket. Even though you will commit a file to your subversion repository as part of this work, there is no official submission associated with lab 1 and you will not be evaluated on it. (You will be evaluated on all subsequent lab exercises.) Follow the steps below and ask questions as needed as you go along. 1. Change to the root directory by typing cd /. Type pwd and ls and have a look around. Type cd with no arguments. Type pwd to see where you are. Try cd .. and pwd. Try cd - and pwd.

2.

Type cd with no arguments. Type ls to see what's there. Type ls -1 (that's the numeral 1) to display the contents of the working directory, one per line. Notice the difference between the output of ls and the output of ls -F. You can combine the effects of the options 1 and F in several ways: try ls -1 -F, ls -F -1, ls -1F and ls -F1. Try ls -lFG. There are many options to the ls command. Type man ls and read about some of them. Press q to exit man.

3.

Change to your home directory. Create a batch of empty files by typing touch file1.empty file2.empty file3.empty file4.empty file5.empty. Type ls *.empty.
1 / 66

Type ls file*. Type ls -lFG file*. Type ls -hla file*. Type rm file*.empty. Type echo *. Type echo *.*. Type echo ~.

4.

Change to your home directory. Create a batch of empty files by typing (note the capitals) Type ls *.empty. Type ls F*.empty. Type ls f*.empty. Type ls [Ff]*.empty. Type ls [Ff].empty. Type rm [Ff]*.empty. Type ls *.empty.
touch FILE1.empty file2.empty FILE3.empty file4.empty FILE5.empty.

5.

Change to your desktop. Type for I in {1..500} ; do touch FILE$I.empty; done. Type ls FILE*.empty. Type rm FILE*.empty.

6.

Type echo "a". Type echo "b". Type echo "c". Type echo "a" > file1.file. Type ls. Type cat file1.file. Type echo "b" >> file1.file. Type ls. Type cat file1.file.
2 / 66

Type echo "c" >> file1.file. Type cat file1.file. Type wc file1.file. Type wc -l file1.file (that's a lowercase l as in, well, lowercase). Type man wc and read a bit. Press q to exit man. Type echo "d" > file1.file. Type wc -l file1.file. What happened? Type cat file1.file. Type rm file1.file.

7.

Type date. Type NOW=`date` (those are backquotes). Type echo NOW. Type echo $NOW. Type pwd. Type CURRDIR=`pwd`. Type echo CURRDIR. Type echo $CURRDIR. Type mkdir -p "$CURRDIR/banana". Type B="$CURRDIR/banana". Type echo $B. Type cd $B. Type cd ~. Type pwd. Type rmdir $B.

8.

Change to your desktop. Check out a local copy of your repository with
svn checkout https://phoenixforge.cs.uchicago.edu/svn/CNET-cs151aut-13

but, when you do so, use your own actual CNet ID, not CNET. At this point, if you don't have a subversion repository for some reason,
3 / 66

please let the TA know, and continue with the exercises as best you can. You can circle back and do the subversion part of Lab 1 once your repository exists. Type ls to make sure the local copy did indeed arrive. Change into the local copy of your repository. Type echo "This is a test." > test.file. Type svn add test.file. Type svn commit test.file -m "Adding a test file.". Type svn mkdir lab1. This will create a directory lab1 inside your repository where you can store your lab 1 work.

9.

Start DrRacket. DrRacket may at some point ask you to choose a "language level." For this week, select the "Beginning Student" level. In order to make use of DrRacket's functional graphics system, you need to "require" an additional graphics library. This requirement is the first line of the code sample below. Type the following code into DrRacket's definitions window:
(require 2htdp/image) (square 50 "solid" "blue") (circle 10 "solid" "purple") (beside (square 50 "solid" "blue") (circle 10 "solid" "purple")) (above (square 50 "solid" "blue") (circle 10 "solid" "purple"))

Click the "Run" button at the top of the DrRacket window, and make sure you understand what you see. Add the following expression to the definitions window:
(beside/align "bottom" (square 50 "solid" "blue") (circle 10 "solid" "purple"))

Run the code again and observe the results. Now, try substituting different numbers in place of 50 and 10, and different colors in place of blue and purple. Try some expressions that use "top" in place of "bottom" in beside/align, and try above/align with "left" and "right". Search DrRacket's help system for 2htdp/image for a full catalog of
4 / 66

image operations. 10. You will now use Racket to render a simple illustration of a landmark of the Chicago skyline, the Willis Tower (formerly the Sears Tower). You will begin with the following crude approximation of the Willis tower. Start a fresh Racket file, and type the following code into the definitions window:
(require 2htdp/image) (define tower (above (rectangle 2 8 "solid" "black") (rectangle 20 100 "solid" "black")))

Save this file under the name lab1.rkt into the lab1 directory you created in the local copy of your repository. Consult images of the Willis tower online, and improve your own rendering of the tower accordingly. 11. To push your lab1.rkt file into your subversion repository, you need to add it first, then commit it. Note that once you've added a file, you do not need to add it again, although you will (usually) commit it again many times. To do this, change into your repository at the command line. Then type these instructions:
$ svn add lab1/lab1.rkt $ svn commit lab1 -m "committing lab1"

5 / 66

CMSC 15100: Lab 2


Wed 10/9, Thurs 10/10
CS151 Coursework | CS151 Home Lab 2 will be collected from your subversion repository on Thursday, October 10, at 11:59pm. Language Level: Beginning Student
;; no libraries to require

As per the box above, set your language level to Beginning Student in DrRacket. You may not require any libraries for these exercises. Please create a lab 2 directory in your subversion repository with
$ svn mkdir lab2

Work in the file lab2/lab2.rkt. It is important that this file has exactly this name and location; we will be looking for your work under this name at collection time. This week, you will practice working with data structures and program design by implementing some common useful operations on calendar dates. We present a formula for computing the day of the week (below). The formula only works for the 20th and 21st centuries, so your dates will be restricted to that range. First write a function leap? to compute whether or not a year is a leap year. Leap years are not simply every fourth year. The precise rule is as follows: A year is a leap year if it is divisible by 4 and not divisible by 100, or if it is divisible by 400. Here is a data definition for dates:
;; a date is a ;; (make-date m d y) where ;; - m is a number in [1, 12], ;; - d is a number in [1, 31], and ;; - y is a number in [1900, 2099] (define-struct date (month day year))
6 / 66

Defining the date structure creates the constructor make-date, which is too permissive: it allows dates like September 31st, or, for that matter, September 32nd. Write the following smart constructor to check the validity of a date before constructing the value.
;; checked-make-date : num num num -> date

In the case of a valid date specification, checked-make-date should return a date. Otherwise, it should raise an error by evaluating the following expression:
(error 'checked-make-date "invalid date")

You can check error cases with check-error (see the documentation). Now write two comparison functions, one to test whether two dates are exactly the same, and one to see if, among two dates, the first occurs before the other. These functions should have the following names and types:
date=? : date date -> boolean date<? : date date -> boolean

We now give the formula for computing the days of the week. We first define a "month adjustment" as follows:
Month 1 2 3 4 5 6 7 8 9 10 11 12 Adjustme nt 0 for leap years, 1 otherwise 3 for leap years, 4 otherwise 4 0 2 5 0 3 6 1 4 6

The day of the week formula is this. Let m, d, and y be the month, day and year of a given date, and let j be the "month adjustment" (per the table above) for the given month and year. Then let n be the following: n = (y - 1900) + j + d + floor(y/4)
7 / 66

where floor(y/4) is the integer quotient of y divided by 4. (For example, floor(401/4) is 100). Let w be the remainder of n divided by 7. Then if w = 0, the day is Sunday; if w = 1, the day is Monday; ...; if w = 6, the day is Saturday. Write
;; day-of-week : date -> string

which returns a string ("Sunday", "Monday", etc.) TIP: Explore the terminal command cal. When you construct tests for this function, you can use cal to check your answers easily. Finally, write a function to generate a convenient string representation of a date.
date->string : date -> string

The date string should have the following form: the day of the week, a space, the date, a space, a three-letter abbreviation of the month, a space, and the year. Some examples follow:
> (date->string (make-date 1 1 2000)) "Saturday 1 Jan 2000" > (date->string (make-date 7 8 1980)) "Tuesday 8 Jul 1980" > (date->string (make-date 10 9 2013)) "Wednesday 9 Oct 2013"

To implement date->string, you will need to use the built-in operations string-append and number->string. Note that string-append, like +, overlay and many other Racket operations, is of variable arity: it consumes 0 or more arguments. All of the arguments to string-append must be strings. Every function must be preceded by a contract and a purpose and be followed by check tests. On this point, we do not differentiate between "helper functions" and "main functions" toss that distinction away: functions are just functions. With respect to subversion: don't forget to commit your work; adding is not enough! When you add a file, you announce to the system your intention to upload that file into the central repository, but only when you commit does that upload actually occur. Remember: add once, commit many times. And commit early and often.

8 / 66

CMSC 15100: Lab 3


Tues 10/15, Wed 10/16
CS151 Coursework | CS151 Home Lab 3 will be collected from your subversion repository on Thursday, October 17, at 11:59pm. Language Level: Intermediate Student
;; no libraries to require

Please create a lab 3 directory in your subversion repository with


$ svn mkdir lab3

Work in the file lab3/lab3.rkt. It is important that this file has exactly this name and location; we will be looking for your work under this name at collection time. This week, you will practice list programming while implementing a linear regression calculator for data sets. The problem is well-suited to being decomposed into a set of related functions. The lab exercise description does not specify too closely which individual functions you should write; this is to give you practice with program design.

Modeling Datasets
You will need two data definitions in order to do this exercise: dataset and lineq (for "linear equation"). A posn (which is a built-in struct) consists simply of an x and a y. A dataset is a list of posns. A lineq is a slope and a y-intercept (m and b from the linear equation form y = mx + b). Write a full data definition, including define-struct, for lineq structures. A linear regression analysis is applied to a set of data; its result is the line that best fits the data (provably so) as it would appear on a twodimensional plot. Your goal today is to write a function linreg whose contract is
;; linreg: dataset -> lineq

Linear Regression
In the following discussion, assume that n is the number of datums in
9 / 66

the given dataset. The best-fit slope for the linear model of a dataset is given by

The y-intercept for the linear model of a dataset is given by

The two formulas above can be expressed as functions, with the following contracts:
;; slope : dataset -> num ;; intercept : dataset -> num

Your design will inevitably involve helper functions. The helper functions you will need to write in order to get linreg to work will mostly be structurally recursive functions that follow the How to Design Programs template closely. When you have finished linreg, you have finished the lab. Commit as usual.

Notes
Linear regression analysis is not meaningful on any data set containing fewer than two points. In such cases, you should raise an error. Define a few simple test datasets for your tests. They'll save space and increase clarity. Use a spreadsheet's SLOPE and INTERCEPT functions to check your test results. Excel, OpenOffice and Google Docs are all suitable choices. You might want to write sum : (listof num) -> num. The mathematical notation in this lab is terse. If you find the notation confusing or unclear, ask TAs and fellow students (it's well within academic honesty boundaries to ask fellow students for help with notation) for explanation.

Acknowledgements
This programming exercise is based on similar exercises developed in collaboration with faculty (of whom I (ams) was one) at the Dwight10 / 66

Englewood School in Englewood, NJ.

11 / 66

CMSC 15100: Lab 4


Tues 10/22, Wed 10/23
CS151 Coursework | CS151 Home Lab 4 will be collected from your subversion repository on Thursday, October 24, at 11:59pm. Language Level: Intermediate Student
(require 2htdp/image) (require 2htdp/batch-io)

Please create a lab 4 directory in your subversion repository with


$ svn mkdir lab4

Work in the file lab4/lab4.rkt. It is important that this file has exactly this name and location; we will be looking for your work under this name at collection time. Today you will build a useful piece of software out of various related components. By combining your linear regression code from last week's exercise, and some new graphics routines you will write today, you will build a tool for consuming a dataset from a text file and producing a visualization of linear regression analysis on that dataset.

Preliminaries
Procure your linear regression code, including the computation of r2 from the last homework, and copy it into a file lab4.rkt. Use the following data file to test your work: discus.csv. The data gives the gold metal distance for discus throws in the Olympic Games from 1900 to 1992. The data is strongly linear. The x values are years since 1900 and the y values are distances in meters. The data was obtained from http://exploringdata.cqu.edu.au/oly_gold.htm (now defunct!). We also encourage you to create your own csv test files and use those in your development as well.

Part 1: Graphics
The first component is a visual tool for linear regression. You will write
12 / 66

a function
;; linreg-image: (listof posn) -> image

that, given a dataset, will produce a plot of the data overlaid with the best-fit line as determined by the analysis, as well as the text of the linear equation. To simplify the assignment, we assume the given datasets all lie entirely in the first quadrant, and in each figure produced, the origin is situated exactly at the lower left corner. Your visual representation of linear regression analysis must include, at a minimum, axes, datapoints, a sketch of the best-fit line found by your analysis, and the equation of the best-fit line computed by your analysis, and r 2. You may break this problem into smaller pieces as you see fit. Here are some of the functions in my own implementation: that returns an image of the positive parts of the x- and y-axes up to the given limit, with the origin at the lower left corner. In my draft implementation, 1 pixel corresponds to one unit on the graph. This results in small images. You may use a different scale as you see fit; just be sure the scale is consistent among all functions.
axes: num num -> image

where the second input to the function is the plot onto which to draw a given point. You may assume that the lower left corner of the given plot image is the origin. The function overlay/xy is helpful, but note its interpretation of the y coordinate differs from the usual plotting interpretation.
plot-point: posn image -> image

which plots a dataset onto the background specified in the function's second input (this can be cleanly implemented with foldr).
plot-dataset: (listof posn) image -> image

For drawing the best fit line on the plot, the 2htdp/image function addline is helpful, but, once again, be mindful of the interpretation of the y coordinate.

Part 2: File Input


Write a function
;; dataset-from-file: string -> (listof posn)

to produce a dataset from a csv file. Use the function read-csv-file from the batch-io library (see the corresponding require at the top of
13 / 66

this page) in combination with string->number.

Part 3: Putting it Together


Finally, you will put the pieces together into a function
;; main: string string -> bool

which will consume the names of an input data file and an output data file, respectively, read the data in from the former and save an image to the latter. Use the 2htdp/image library function
;; save-image : image string -> boolean

(whose string argument is a file name) to save the image.

Samples
As stated above, your image must include axes, datapoints, the best-fit line, and the text of the linear equation and r2 , as follows:

An image like this is sufficient for the purposes of this exercise. If you are feeling more ambitious, you can enrich the appearance of your image with various features, as in the following illustration:

14 / 66

Comments
Please note that we may test your code on a different data set than this. If so, all the data will lie in the first quadrant, as here. Don't worry about intelligent placement of the text of the analysis; if it covers part of the data or the trendline, so be it.

15 / 66

CMSC 15100: Lab 5


Tues 11/5, Wed 11/6
CS151 Coursework | CS151 Home Lab 5 will be collected from your subversion repository on Thursday, November 7, at 11:59pm. Language Level: Intermediate Student with lambda
(require 2htdp/image) (require racket/match)

Please create a lab 5 directory in your subversion repository with


$ svn mkdir lab5

Work in the file lab5/lab5.rkt. It is important that this file has exactly this name and location; we will be looking for your work under this name at collection time. Today you will implement a address book data structure as a binary search tree of "vcard" structures (loosely related to the actual vCard file format); furthermore you will write function to build images of those trees to be able to visualize them clearly.

Binary Search Tree Basics


Copy the following data definitions into your lab5.rkt.
;; a vcard is a (make-vcard lname fname email tel) ;; where lname, fname, email and tel are strings (define-struct vcard (lname fname email tel)) ;; a vtree is either ;; - empty, or ;; - (make-vtree c lsub rsub) where c is a vcard, lsub and rsub are vtrees (define-struct vtree (c lsub rsub))

Implement the following comparison functions, which must consider only last name and first name in their comparisons.
;; vcard<? : vcard vcard -> bool ;; compare vcard by last name, then first name (and no other fields) ;; vcard=? : vcard vcard -> bool ;; compare vcard by last name and first name (and no other fields)

Write a function to insert vcards into a vtree using the comparison


16 / 66

functions given above. Vcards that are less than (per the comparison function above) the vcard at the root belong in the left subtree; vcards greater than the root go into the right subtree. The policy on vcards that are equal to another vcard in the tree (per last name and first name) is as follows: if a vcard is inserted into a vtree where a vcard with the same name is already present in the tree, the new vcard (the one being inserted) should replace the existing one.
;; insert : vcard vtree -> vtree ;; note: when the name on the given vcard is already present in the vtree, ;; the new vcard displaces the old one

Write a function, given a last name and a first name (in that order) and a vtree, return either the vcard in the tree with those names, or false if there is no such vcard present. Note the return type in the contract of this function: the + notation means the function returns either a vcard or the value false.
;; find : string string vtree -> (+ vcard false) ;; note: *do not* search both trees recursively, which defeats the purpose ;; of having constructed a search tree in the first place.

The following functions will visualize the vcard, providing an easy way to observe the structure of the object. Read the comments carefully, which specify how you will go about it.
;; ;; ;; ;; ;; ;; ;; ;; ;; vcard-img : num vcard -> image Draw a rectangular image of the vcard. The given number is the minimum width of the image. Ensure the rectangle is at least that wide. Include all four elements on the image of the card: the first and last names, the email address, and the phone number. Within these constraints, you have freedom to design how a vcard is visualized; you need not ask us lots of questions about how precisely they should look.

;; vtree-img : vtree -> image ;; - The image of the empty tree must not be the empty image -- it ;; must be something visible (we don't care exactly what). In my ;; (Adam's) draft implementation, I use ;; (rectangle 40 10 "solid" "maroon") ;; - Follow this algorithm to draw the tree: recursively draw both ;; subtrees; put them beside one another, aligned at the top; draw ;; the root vcard above that, where with min width of the root vcard ;;; is the total width of the images of the two subtrees.

Comments
You may find pattern matching especially convenient when programming with trees.

17 / 66

CMSC 15100: Lab 6


Tues 11/12, Wed 11/13
CS151 Coursework | CS151 Home Lab 6 will be collected from your subversion repository on Thursday, November 14, at 11:59pm. Language Level: Intermediate Student with lambda
(require 2htdp/image) (require racket/match) (require 2htdp/universe)

Please create a lab 6 directory in your subversion repository with


$ svn mkdir lab6

Work in the file lab6/lab6.rkt. It is important that this file has exactly this name and location; we will be looking for your work under this name at collection time. Today you will implement an interactive click-and-tally counter to count objects in an image. With simple modifications, you will be able to use this tool for any type of object in any image. However, today, for a bit of local color, you will use the tool to count geese in nearby Washington Park.

18 / 66

Universe Basics
Today we will use DrRacket's "universe" library to build interactive software. Universe is a capacious library with many interesting features; we will zero in on a few fundamental ones in today's exercise. The first thing one needs to do to create a universe project is to decide on a model of the state of the world. It can be any data -- a number, a Boolean constant, a list -- or a special data structure containing a mixture of relevant items. We will take the latter approach and define a world as follows.
;; a world is a (make-world i cs r m show? done?) where ;; - i is an image, ;; - cs (the clicks) is a (listof posn), ;; - r is a num, ;; - m is a string, ;; - show? is a bool, and ;; - done? is a bool (define-struct world (i cs r m show? done?))

(Actually naming this struct a world -- or defining a struct at all -- is not required, as long as your code is internally consistent.) Copy this data definition into your source file. Some comments: i is the image being clicked upon. cs is the current list of clicks, initially empty. r is the radius of each click circle. m is the current message. It can be whatever you want it to be (within the bounds of taste and decency, please). We suggest you use it to give feedback to the user on their most recent action.
19 / 66

show? indicates the current state of "show mode" (initially true) (more on this below). done? indicates doneness (initially false). Once you have decided on what a world is, you can create a "new universe," as it were, with the function big-bang. big-bang takes the initial state of the world as its argument, and it is then followed by a number of clauses, in any order, defining how the universe behaves. What happens in a universe is essentially this: the world begins in its initial state, and as the user takes actions such as clicking the mouse or typing on the keyboard, a new world, in a (possibly) different state, is constructed as the result of the action. Furthermore, the current world is redrawn in its window when it differs from its world predecessor. Here is a sketch of a call to big-bang, with types, displayed inside angle brackets, in place of actual values. Look at the types carefully -- in each case the type tells you a great deal about the value that goes there.
(big-bang <world> [to-draw <world -> image>] [on-key <world key -> world>] [on-mouse <world num num mouse-event -> world>] [stop-when <world -> bool>])

Comments on the key and mouse-event types: both key values and mouse-event values are strings, so string comparison and string matching work on them as such. For simple alphabetic and symbolic keys, a key is simply a string containing the corresponding character: "q", "n", "+" and so on. You can use string=? or key=? to inspect a key (both operators do the same thing), or pattern match against string constants, as you prefer. A mouse-event is one of the following strings: "button-down", "button-up", "drag", "move", "enter", or "leave". You can also use string=? or pattern matching on mouse-event values (the function mouse-event=? seems not to exist). The only mouse-event that will change the world in the current exercise is "button-up".

The Appearance of the World


Visually, the world should appear as the image, whose upper-left corner should be at the upper-left corner of its window, and directly below it, various textual presentations as follows. Underneath the image, it must display the number of clicks in the current click list, and it must display the current message (of the world). Underneath those elements, you may, if you wish, display instructions on how to use the software. Friends: we don't care exactly how you arrange the elements or what precisely they look like, so please don't shower us with questions along those lines. Just use common sense and your own judgment. In other words: make it look nice. If show mode is true, any clicks in the current click list should be
20 / 66

displayed at their locations by circles with the given current click radius (and in any color you like). If show mode is false, no clicks should be displayed. When coloring the circles, you might like to use the alpha channel for some transparency (it's a pleasing effect).

Event Handling
Write a click handler to behave as follows: On "button-up", if the world is in show mode and the click is within the bounds of the image, then the current click should be added to list of clicks. Write a key handler that responds to keys in the following ways: (related to the common key for Undo) should erase the most recent click from the list. You should be able to type z repeatedly and erase more and more clicks as you go. Striking z when there are no clicks should have no effect; specifically, it must not crash the program.
z c t

should clear all clicks from the current click list.

should toggle the "show mode." When show mode is on, all clicks should be displayed and any new clicks should be added to the list. When show mode is off, no clicks should be displayed and new clicks should be ignored. should increment the radius of the click circles by 1. There is no maximum click circle radius (yay!).
+

should decrement the radius of the click circles by 1, but the click circle radius must never go lower than 1.
q

should quit.

To run the program , include a call to big-bang at the bottom of the source code. When DrRacket gets there, it will evaluate the expression and start the universe running in its own window.

Sample Images
You can import either or both of these images into your code using DrRacket's "Insert | Image..." menu item. Then you will be free to include either one in your initial world. Newbies might prefer to count geese in this photo:

21 / 66

For extreme, unapologetic, your-friends-pretend-not-to-care-butactually-wish-they-were-you goose clicking, try this:

(All photos taken Tues Nov 12 2013 around 9am in Washington Park by Adam Shaw.)

22 / 66

CMSC 15100: Lab 7


Tues 11/19, Wed 11/20
CS151 Coursework | CS151 Home Lab 7 will be collected from your subversion repository on Thursday, November 21, at 11:59pm. This is your last lab exercise of the quarter. Say thanks to your lab TA on the way out! Erik, Nedelina, Nick and Charisee each work hard on your behalf. Language Level: Intermediate Student with lambda
(require 2htdp/image) (require racket/match)

Please create a lab 7 directory in your subversion repository with


$ svn mkdir lab7

Work in the file lab7/lab7.rkt. It is important that this file has exactly this name and location; we will be looking for your work under this name at collection time.

Gobblers Symmetries
Gobblet Gobblers is full of symmetries. For example, consider these two boards B1 and B2:

<="" td=""> B1 B2

<="" td="">

Assuming the squares on the board have indices 0, 1, 2, ..., 8 (as in our project code) these boards are represented differently, so, in as sense, they are different. However, from the point of view of game strategy, they are identical; one is simply a clockwise rotation of the other by 90 degrees. Each board can be rotated in this way three times before coming back around to its original position.
23 / 66

Each board can not only be rotated without changing its strategic meaning; it can also be flipped (or mirrored) as follows. Here is B1 from above, and its "vertical flip" or "vertical mirroring," labeled B3.

<="" td=""> B1 B3

<="" td="">

In total, each board can by rotated and flipped into as many as eight distinct positions, like so:

This set of boards represents an equivalence class with respect to strategy; that is, any strategy that applies to any one of these boards applies equally well to any other. The reason each such set contains as many as eight rather than eight positions is that some boards are identical under rotating and flipping; consider any board with pieces only in the center square, for example. There is still more symmetry to exploit. If the colors on a board are inverted, the resulting board is exactly the same as the original board, expect for the fact that the interests of blue and orange are swapped. That is to say, that the equivalence class below, the inversion of the equivalence class above, is identical to its inversion except for the fact that the players' interests are exactly exchanged.

24 / 66

The strategy logic you will write for project 3 will exploit these symmetries and inversions in order to do more computation in a shorter time and, ultimately, choose better Gobblers moves.

Exercise
In addition to the data definitions provided (since project 1) for piece and board, you must write a board-set data definition, and implement the following operations:
;; board-set-insert : board board-set -> board-set ;; board-set-member? : board board-set -> bool

For your set implementation, simply use a list of boards. Faster set implementations exist, of course, but for such small sets as these, where so few operations* are needed, and in the interest of expedience, lists will do. Please note that it is not necessary to define a struct to define board-set. The important feature of the set is that it must never contain duplicate values. *A proper set implementation would be crafted to support efficient common set operations like union, intersection, and set difference. Write the function board-invert to switch all the colors of all pieces on the board.
;; board-invert : board -> board

Write the function board-equiv-class to build the equivalence class for the given board. Every board is in its own equivalence class. A given board's equivalence class contains all clockwise rotations* of the original board, and all rotations of the vertical flip of the original board.
;; board-equiv-class : board -> board-set

*A clockwise rotation is as illustrated above in the table showing B1 and B2. Write board-equiv?. Two boards are equivalent if they inhabit the same equivalence class.
;; board-equiv? : board board -> bool

25 / 66

Remarks
Visualization, though very helpful, is not part of this exercise. You may include visualization code if you like, including code pasted in from the project 2 seed code, but it will not be graded. These problems all have terse solutions. If you find yourself writing hundreds upon hundreds of lines of code, reconsider your approach. Look for commonalities, and use and/or write higher-order functions where possible. As a rule of thumb, 200 lines or so should suffice for the entire exercise (not counting any visualization code). You might wish to include the following definitions in your code for convenience:
(define (define (define (define (define (define o1 o2 o3 b1 b2 b3 (make-piece (make-piece (make-piece (make-piece (make-piece (make-piece 1 2 3 1 2 3 'orange)) 'orange)) 'orange)) 'blue)) 'blue)) 'blue))

Enjoy!

26 / 66

CMSC 15100: Introduction to Computer Science I


University of Chicago, Autumn 2012
CS151 Coursework | CS151 Home

Homework 1
Homework 1 is due Monday, October 7, 2013 at 11:59pm. Beginning Student w/ List Abbrev.
(require 2htdp/image)

These exercises will help you develop the ability to write simple functions in Racket, become familiar with the DrRacket programming environment, and practice the particular discipline of program design taught by and advocated for by the textbook. You will submit your work with filename hw1.rkt in a directory called hw1. The following commands, when run from within a copy of your repository, will get you started. Note the third instruction, touch hw1.rkt, will create a (blank) file of that name. Also, the command cd .. goes "up" one directory from the current working directory. $ svn mkdir hw1 $ cd hw1 $ touch hw1.rkt $ svn add hw1.rkt $ cd .. $ svn commit hw1 -m "getting ready for homework 1" After having done this, work in the file hw1/hw1.rkt At the moment this assignment is published, we have not yet taught you all you need to know to complete this work. Friday's lecture, in combination with the text, will provide you everything you need to know. For the present, do not worry about functions being passed bogus inputs, e.g., a sphere with a negative radius. We will deal with erroneous arguments to functions later in the quarter.
27 / 66

Homework Problems
For every function you write, you must write a contract, a purpose, and tests with check-* utilities. Do write helper functions where it seems like a good idea to do so. All functions need contracts, purposes and tests,* even if they are "only" helper functions. *Note that writing check-expect tests for the flag-drawing functions is not really possible. In cases such as the flag functions, namely, functions that produce complex images, "eyeball tests" (which we will discuss in class) are sufficient.

Problem 1
Write the following conversion functions:
lb->kg mi->km f->c

and kg->lb to convert between pounds and kilograms, and km->mi to convert between miles and kilometers, and

and c->f to convert between degrees Fahrenheit and degrees Celsius.

Problem 2
Three numbers a, b and c are a Pythagorean triple when a2 + b2 = c 2 . For example, (3, 4, 5) is a Pythagorean triple, as is (4, 3, 5), but (3, 4, 6) is not. Write the function pythagorean? to consume three numbers and return true if they are a Pythagorean triple. You may assume that the numbers entered by the user are all positive integers. Furthermore, assume that the third argument to the function is the largest; that is, it is the would-be hypotenuse of the group.

Problem 3
Assume a forty-hour work week. A worker with hourly wage w (dollars per hour) makes w dollars for each hour up to an including the fortieth, and one and a half times w for all hours over forty. This arrangement is sometimes known as "time-and-a-half overtime." Write the function weekly-pay. It must consume exactly two arguments, both numbers: a worker's wage in dollars per hour, and a number of hours. It must compute the total amount, in dollars, the worker earned that week, assuming a time-and-a-half overtime compensation agreement.

Problem 4
Assume the local convention for tipping cab drivers is to add a 15% tip to the fare, and then round up to the nearest dollar. By this scheme, for a cab fare of $8, the fare plus tip is $10, and for a cab fare of $16, the fare
28 / 66

plus tip is $19. Write the function cab-fare-with-tip. It should consume one number, a cab fare in dollars.

Problem 5
Let d(n) be the number of digits (base 10) in nn. Its input must be a positive integer. Write the function d with type num -> num. In the implementation of d, you may at no point convert a number to a string. In other words, you must solve this problem using numerical operations exclusively.

Problem 6
A spherical shell is the volume enclosed between two concentric spheres. In other words, it is the 3-dimensional generalization of the annulus. Real-world volumes that are similar to spherical shells include tennis balls and coconuts. Let a hemishell be the volume obtained by cutting a spherical shell exactly in half. The following is an illustration of a hemishell.

Write two functions to compute with hemishells: hemishell-volume and hemishell-surface-area. Each function must consume two numbers, the radius of the outer sphere and the thickness of the hemishell wall, in that order. The functions may assume the numbers are sensible: that is, that the radius and thickness are both positive numbers, and that the thickness does not exceed the radius. In your computations, use built in pi for the value of . Two hints: 1) write helper functions, and 2) subtract. Furthermore, make sure that you've considered the whole surface in computing the surface area.

Problem 7
Write functions to draw the flags of Norway and Iceland, given the width in pixels of the desired image. Assume the ratios in the two flags are the same, that is, that they have exactly the same pattern in different

29 / 66

are the same, that is, that they have exactly the same pattern in different colors (in truth the ratios in the two flags differ very slightly). Hint: use overlay/align.

Flags of Norway (left) and Iceland.

Problem 8
Write the function wider? to consume a pair of images and produce true when the first image is wider than the second. Similarly, write the function narrower?. These have concise implementations. You may call one from the other, but be careful.

Submit Your Work


Submit your work by committing hw1/hw1.rkt to your CS151 subversion repository by Monday, October 7 at 11:59pm. All your work must be contained in that one file. You should commit your work early and often. Intermediate commits that is, commits made along the way to the final commit are strongly encouraged; only your last commit before the deadline will be evaluated by your graders.

30 / 66

CMSC 15100: Introduction to Computer Science I


University of Chicago, Autumn 2012
CS151 Coursework | CS151 Home

Homework 2
Homework 2 is due Monday, October 14, 2013 at 11:59pm. Intermediate Student
(require 2htdp/image)

These exercises will give you practice designing compound data structures and computing with them, and also writing various simple functions on different types of lists. Please submit your work with filename hw2.rkt in a directory called hw2. At the moment this assignment is published, we have not yet taught you all you need to know to complete this work. Friday's lecture, in combination with the text, will provide you everything you need to know.

Homework Problems
For every function you write, you must write a contract, a purpose, and tests with check-* utilities. Do write helper functions where it seems like a good idea to do so. All functions need contracts, purposes and tests, even if they are "only" helper functions.

Problem 1
First, write the data definition for a compound data structure to represent vectors in 3-space. The structure should be called vec3 and its components x, y and z. Once you have defined the vec3 structure, define the following standard vector operations on it. We have given the name and a brief description of each. Where we have omitted the type of the operation, decide on the correct type and write the precise contract for each operation on your own.
vec3-negate

negate all components of the vector add two vectors,


31 / 66

vec3-add

yielding a new vector


vec3-sub

subtract the second vector from the first scale all components of the vector by the given scalar compute the dot product of two vectors, which is the sum of the componentwise products compute the magnitude (length) of a vector normalize the vector: that is, compute a unit vector pointing in the same direction

vec3-scale : num vec3 -> vec3

vec3-dot

vec3-mag

vec3-norm

Problem 2
The names and types of various list operations are given below. Please implement them. Use the types given to understand how each is designed to behave.
halves : (listof num) -> (listof num) multiply-by : num (listof num) -> (listof num)

divide all numbers by two multiply all numbers by the given scalar; for example, (multiply-by 7 (list 1 2 3)) must produce (list 7 14 21) keep negative numbers, discard others; for example, (negatives? (list 0 -1 1 -2 3)) must produce (list -1 -2) keep numbers above threshold, discard others; for example must produce (list 8 9 8)

negatives? : (listof num) -> (listof num)

larger-than : num (listof num) -> (listof num)

(larger-than 7 (list 6 7 8 9 8 7 6))

taller-than : num (listof image) -> (listof image)

keep images taller than given threshold, discard others


32 / 66

list-product : (listof num) -> num

compute the product of all numbers in the list must return true is exactly one item in the list is true, false otherwise stack all images on top of one another, with the first image at the top, the second image immediately under it, etc. Note that there is a special built-in value empty-image which is suitable for the base case here. build a list of lists containing "that many" of each; for example, (that-many (list 2 1 3)) must produce You may assume every number in the input list is nonnegative.

list-xor : (listof bool) -> bool

tower : (listof image) -> image

that-many : (listof num) -> (listof (listof num))

(list (list 2 2) (list 1) (list 3 3 3)).

Submit Your Work


Submit your work by committing hw2/hw2.rkt to your CS151 subversion repository by Monday, October 14 at 11:59pm. All your work must be contained in that one file. A reminder: commit your work early and often. Intermediate commits that is, commits made along the way to the final commit are strongly encouraged; only your last commit before the deadline will be evaluated by your graders.

33 / 66

CMSC 15100: Introduction to Computer Science I


University of Chicago, Autumn 2012
CS151 Coursework | CS151 Home

Homework 3
Homework 3 is due Monday, October 21, 2013 at 11:59pm. Intermediate Student
(require 2htdp/image)

Please submit your work with filename hw3.rkt in a directory in your repository called hw3.

Homework Problems
For every function you write, you must write a contract, a purpose, and tests with check-* utilities, with the usual exception for functions that produce complex images, for which "eyeball tests" are appropriate. Write helper functions where they are needed. All functions need contracts, purposes and tests, even if they are "only" helper functions.

Problem 1
Write concat-with : string (listof string) -> string to concatenate all strings with the given separator. Examples:
(concat-with "," (list "Crosby" "Stills" "Nash")) => "Crosby,Stills,Nash" (concat-with "<|>" (list "Crosby" "Stills" "Nash")) => "Crosby<|>Stills<|>Nash" (concat-with "<|>" (list "Crosby")) => "Crosby"

Problem 2
Write chessboard : num num color color -> image. The function should consume the following arguments, in order: the number of squares in each row/column, the side length of each individual square,
34 / 66

the light square color, and the dark square color. A chessboard should be oriented such that the lower right square is light colored.

Problem 3
Write pascal : num -> (listof (listof num)) to compute the first n rows of Pascal's triangle (for n 0).

Problem 4
Linear regression analysis computes a slope and an intercept no matter what dataset it's given, even when the data is not really linear at all. (Think of points lying on a parabola, or just randomly scattered about.) A full linear regression analysis includes the computation of a linear correlation coefficient that quantifies the strength of the data's linearity. This coefficient is usually referred to as r and is a measure, roughly speaking, of "how linear the data is." Typically a linear regression analysis provides the value r 2, which lies on the interval [0,1]. The closer r 2 is to 1, the stronger the correlation between the data and the line modeling the data. An r 2 close to 0 means that the analysis has calculated a linear equation that bears no meaningful relationship to the data at hand. An analysis will be a struct consisting of two parts: a lineq and a num, the latter being the value of r 2. Write a full linear regression analysis with the contract
;; full-linreg: dataset -> analysis

Copy your lab 3 code to compute the slope and intercept. The linear correlation coefficient r is given by the following:

An overbar indicates the arithmetic mean of the term underneath. A lowercase sigma () indicates the standard deviation (as defined below) of the subscript term. The standard deviation is the square root of the variance, where the variance of a set of numbers is the is the mean of the squares minus the square of the mean. Note : the formula above gives r; the full analysis returns r 2 .
35 / 66

You can use the common spreadsheet function RSQ to check your results.

Problem 5
Here is a function to draw an asymmetric square design. Here is (design 128):

and here is the code.


;; design : num -> img ;; draw square design, not symmetrical, with given side length (define (design s) (overlay (square s "outline" "maroon") (above (beside (rectangle (/ s 2) 1 "solid" "darkgray") (overlay (circle (/ s 6) "solid" "gray") (circle (/ s 5) "solid" "maroon"))) (rectangle 1 (/ s 2) "solid" "darkgray")) (square s "solid" "darkgray")))

Copy and paste this code into your homework file. Write a function recursive-tiles : num -> img to consume a given side length and produce the following recursive tiling of the design. The image below is the result of calling (recursive-tiles 512).

36 / 66

This is not a fractal (why not?). By inspecting the image carefully, you can discover everything you need to know about its construction. You should write helper functions. Make sure the image you produce is of the right height and width:
> (image-width (recursive-tiles 512)) 512 > (image-height (recursive-tiles 512)) 512

In your implementation of recursive-tiles (and helpers), never call design with an argument less than 2. Note: arguments that are powers of 2 are best for recursive-tiles, because of the repeated halvings of the number. We will not judge your code for how it works on numbers not powers of 2. Note: it shouldn't take too long for the system to draw this. On my machine, which is a 2011 MacBook Pro, I get
> (time (recursive-tiles 512)) cpu time: 1141 real time: 1164 gc time: 155

These units are milleseconds, so that's a bit more than 1 second. If your program takes much longer than that, something is probably wrong. You will boost your program's performance by defining an image
37 / 66

locally when you know you need more than one identical copy of it (and that is the case here in many instances). This exercise was inspired by a section in Abelson and Sussman's Structure and Interpretation of Computer Programs.

38 / 66

CMSC 15100: Introduction to Computer Science I


University of Chicago, Autumn 2012
CS151 Coursework | CS151 Home

Homework 4
Homework 4 is due Monday, October 28, 2013 at 11:59pm. Intermediate Student with lambda
(require 2htdp/image) (require racket/match)

Please submit your work with filename hw4.rkt in a directory in your repository called hw4.

Homework Problems
For every function you write, you must write a contract, a purpose, and tests with check-* utilities, with the usual exception for functions that produce complex images, for which "eyeball tests" are appropriate. Write helper functions where they are needed. All functions need contracts, purposes and tests, even if they are "only" helper functions.

Problem 1
Implement the following functions as calls to map, filter, foldr or some combination thereof. Write helper functions when needed. You may use lambda where appropriate. Copy over your vec3 data definition and relevant operations for use in certain problems below.
;; sum-of-squares : (listof num) -> num

add the squares of all numbers in a list


;; within : num (listof posn) -> (list of posn)

keep those points whose distance to the origin is strictly less than the given threshold, discard the rest
;; vec3-sum : (listof vec3) -> vec3

compute the sum of all given vectors under vector addition


39 / 66

;; vec3-total-magnitude : (listof vec3) -> num

compute the total magnitude of all given vectors


;; wider-than : num (listof image) -> (listof image)

keep the images wider than the given threshold, discard the rest
;; total-area : (listof image) -> num

using the image-width and image-height of each image in the list, compute the total area in pixels of all images in the list

Problem 2
Consider the following recursive functions, each of which consumes a natural number.
;; sum-upto : nat -> nat ;; compute the sum from 0 to n (define (sum-upto n) (cond [(zero? n) 0] [(positive? n) (+ n (sum-upto (sub1 n)))])) ;; string-upto : nat -> string ;; build the string of naturals up to given bound ;; ex: (string-upto 3) => "0123" (define (string-upto n) (cond [(zero? n) "0"] [(positive? n) (string-append (string-upto (sub1 n)) (number->string n))])) ;; countdown : nat -> (listof nat) ;; build the list of naturals from n down to 0 (define (countdown n) (cond [(zero? n) (list 0)] [(positive? n) (cons n (countdown (sub1 n)))]))

These functions share a basic structure in common. Just as it is possible to generalize various common list-consuming functions with foldr, it is possible to generalize over these natural number functions as well. Write natfold : (nat alpha -> alpha) alpha nat -> alpha to generalize these functions. Then reimplement sum-upto, string-upto and countdown, as above, as calls to natfold. Furthermore, implement fact : num -> num (for factorial) as a call to natfold.

Problem 3
A numerical integral is computed by fitting rectangles (or trapezoids) under the curve of a function, and computing the total area of those rectangles. Such integrals are known as Riemann sums. Here is a sketch of a left-rectangle Riemann sum:

40 / 66

Write a function riemann/lr : (num -> num) num num num -> num to compute the left-rectangle integral of a given function. The four arguments to riemann/lr must be a function, the width of each rectangle under the curve, the left endpoint of the integral, and the right endpoint of the integral. For the purposes of this problem, the integral from left endpoint a to right endpoint b is 0 where a b. You may use this fact as the terminating base case in your recursion. Note that the results are increasingly accurate as the width of the rectangles decreases. Here are some examples to check against:
> (riemann/lr identity 0.1 0 10) 49.5 > (riemann/lr identity 0.01 0 10) 49.95 > (riemann/lr identity 0.001 0 10) 49.995 > (riemann/lr cos 0.1 0 (/ pi 2)) #i1.0502004622913053 > (riemann/lr cos 0.01 0 (/ pi 2)) #i1.0049953312208086 > (riemann/lr cos 0.001 0 (/ pi 2)) #i1.0004999977618816 > (riemann/lr sin 0.1 0 pi) #i1.999547959712598 > (riemann/lr sin 0.01 0 pi) #i1.9999900283082455 > (riemann/lr sin 0.001 0 pi) #i1.9999999540409892

Problem 4
Write the function show-pascal : nat color color -> image to render an image of the first n rows of Pascal's triangle. Each row must display each number in order left-to-right, and each row must appear centered above the row below it. The color arguments are the colors to use to display the odd numbers and the even numbers, respectively.
41 / 66

Comments: The image of 0 rows of Pascal's triangle is the empty image. Use text and number->string to render images of numbers. The functions odd? and even? are built in; feel free to use them. You may use a very small font to render the numbers, so you can see more of the image at once. Yes, you may copy your Pascal's triangle code from last week.

Problem 5
Write a function cal : num num -> img to consume a month and a year and produce a calendar image for that month. For the month argument, 1 means January, 2 February, and so on until 12 for December. You need only deal with the 20th and 21st centuries. You may copy code from previous weeks into the current homework as you see fit. The calendar image must meet the following requirements: the very top of the image should display the month and year as text, (e.g., October 2013), the first row in the calendar itself must display abbreviations for the seven days of the week, starting with Sunday at the left edge, all days must appear as numbered boxes, and the first row of days must be padded with 0 or more empty boxes at the left, as needed. Please note we are not specifying the size of the image produced by cal. Use your judgment, and make sure the image and the fonts are large enough that we can easily see all details and read all text. To check the accuracy of your results (with respect to the dates), you will find it helpful to use the built-in command cal in a console. Think carefully about how to write this function. Do not write an enorm ous, brute-force im plem entation where you consider every possible case in turn. Seek a solution that is both concise and elegant (there are many).

42 / 66

CMSC 15100: Introduction to Computer Science I


University of Chicago, Autumn 2012
CS151 Coursework | CS151 Home

Homework 5
Homework 5 is due Monday, November 11, 2013 at 11:59pm. Intermediate Student with lambda
(require 2htdp/image) (require racket/match)

Please submit your work with filename hw5.rkt in a directory in your repository called hw5.

Homework Problems
For every function you write, you must write a contract, a purpose, and tests with check-* utilities, with the usual exception for functions that produce complex images, for which "eyeball tests" are appropriate. Write helper functions where they are needed. All functions need contracts, purposes and tests, even if they are "only" helper functions.

Polymorphic Pairs
You will need to construct pairs in various places throughout this work. Copy the following data definition into your hw5.rkt:
;; a (pair ) is a (make-pair x y) ;; where x is an , y is a (define-struct pair (first second))

Problem 1
We present selection sort as an algorithm for sorting integers into ascending order (without loss of generality). The algorithm is as follows. The empty list is trivially sorted, so simply return it. For non empty lists containing n>0 elements, proceed as follows.
43 / 66

Split the list into two parts: the minimum, and a list of length n-1 containing everything but the minimum. Recursively sort the latter list, and cons the minimum onto the front of it. Return the result. To implement selection sort, write the following operation first:
;; select-min : (listof num) -> (pair num (listof num))

Make sure that the time cost of select-min is linear and not exponential (or, for that matter, anything worse than linear). Having written select-min, write
;; selection-sort : (listof num) -> (listof num)

The following is an unofficial part of this work, with nothing to submit. Without Googling or running off to Wikipedia, give some thought to the time cost needed to run selection sort. Does it seem like a fast sorting algorithm? Does it remind you of any other sorting algorithms we've seen? Try to analyze its cost on paper, with a recurrence relation.

Problem 2
As presented in class, quicksort rearranges lists of numbers into ascending order. Generalize quicksort so that it can sort any kind of data into any order, given an appropriate comparison test, by writing the following higher-order sorting function:
;; quicksort-by : ( -> bool) (listof ) -> (listof )

Use quicksort-by to implement the following sorting functions:


desc-sort : (listof num) -> (listof num)

numbers from largest to smallest.

to order a list of

If a name is a structure from (define-struct name (fname lname)), write name-sort : (listof name) -> (listof name) to order a list of names in standard last name, first name alphabetical order. (Note string<? is built in, and you may use it.)
img-sort : (listof image) -> (listof image)

images from tallest to shortest.

to order a list of

Problem 3
In this problem, you will use binary search trees to implement sets of colors. Write functions to compare colors as follows:
;; color<? : color color -> bool ;; color=? : color color -> bool
44 / 66

For the first color to be "less than" the second, its red component must be less than the other's, or the reds must be equal and the green must be less than the other's, or the reds and greens must be equal and the blue must be less than the other's. Otherwise, the first color is not less than the second. These ordering functions are necessary to build binary search trees of colors, per the following data definition:
;; a cct ("color count tree") is either ;; - empty, or ;; - a (make-cct c n lsub rsub) ;; where c is a color, n is a number, and lsub and rsub are ccts (define-struct cct (c n lsub rsub))

A well-formed cct is constructed such that every color in the left subtree is less than the root, and every color in the right subtree is greater than the root. The number n at each node is a count, to be discussed further below. Write a function cct-insert : color cct -> cct to insert a color into a cct. To insert into a given nonempty tree, if a color is less than the root, it should be inserted into the left subtree; if a color is greater than the root, it should be inserted into the right subtree; and if a color is equal to the root, the count at the root node should be increased by one. In this way, a cct tracks not only what colors are in a list of colors, but also how many times each color appears. To insert a color into an empty tree, construct a singleton tree with two empty subtrees and count 1. Having written cct-insert, write
;; image->cct : image -> cct

to build a cct from the pixels of a given image. For example, the cct of (square 10 "solid" (make-color 255 0 0)) is a singleton tree with one color node with count 100. Also, write
;; color-count : color cct -> num

to return the count corresponding to the color in the given cct. Take care not to search both subtrees recursively at every step (which forfeits the benefit of the search tree structure). Return 0 when the color is not present in the tree.

Problem 4
Run-length encoding is a data-compression scheme for lists. A runlength encoded list is a list of pairs, whereby each pair includes an item of data and a number, indicating how many of that item there are in the current "run."
45 / 66

For example, consider the following list of nine items.


(list 'A 'A 'B 'B 'B 'B 'C 'C 'C)

This list contains a run of two As, then 4 Bs, then three Cs. Its run-length encoded equivalent is as follows:
(list (make-pair 'A 2) (make-pair 'B 4) (make-pair 'C 3))

Run-length encoding can compress certain lists extremely well, others not at all. Think of the characteristics of a list that make it either well or poorly suited to run-length encoding. Implement the following functions:
;; rle : ( -> bool) (listof ) -> (listof (pair num)) ;; rld : (listof (pair num)) -> (listof )

The function rle (run-length encode) must consume two arguments: an equality test on items of some type and a list of , and produce the corresponding run-length encoding. For the example in the last paragraph, the run-length encoding is written
(rle symbol=? (list 'A 'A 'B 'B 'B 'B 'C 'C 'C))

The second function rld (run-length decode) should perform the inverse operation. Note that no equality function is necessary to perform this operation.

Problem 5
(This problem is closely related to lectures given in week 3.) Many interesting image transformations can be done a pixel at a time. For example, color photographs can be converted to black and white by applying the same operation to each pixel. In such transformations, the same infrastructure -- the code that unfurls the image into a list of colors, transforms all those colors, then packs the colors back into an image -- can be shared. First, write a higher-order function
;; map-image : (color -> color) image -> image

This function must call image->color-list, map and color-list->bitmap. The luminance of a color is a measure of its brightness. Given color components r, g, and b, the luminance is calculated according to the following formula: 0.2126 * r + 0.7152 * g + 0.0722 * b Write the function luminance : color -> num to compute this number for any color.
46 / 66

Implement the following pixel-by-pixel transformations as calls to mapimage (and see the remarks that follow below):
grayscale : image -> image negative : image -> image grayscale-negative : image -> image

Remarks: To construct a grayscale image, for each color c , compute its luminance m and replace it with a color (a shade of gray) whose red, green and blue components are each m (or, more precisely, the integer nearest m). To construct a negative, replace each color (r,g,b) with its complement (255-r,255-g,255-b). A grayscale negative is what it sounds like, per the two definitions above. Take care in your implementation of grayscale-negative that, for efficiency's sake, you traverse each pixel only once, not twice. That is to say, even though this function can be correctly implemented as (grayscale (negative i)) for some image i, you should avoid that implementation because it makes two complete passes over the image in question.

47 / 66

CMSC 15100: Introduction to Computer Science I


University of Chicago, Autumn 2012
CS151 Coursework | CS151 Home

Homework 6
Homework 6 is due Tuesday, November 26, 2013 at 11:59pm. Advanced Student
(require racket/match)

This week, you should submit two files. Please submit a pdf for problem 1, named hw6-p1.pdf. Please submit code for problems 2 and 3 as usual in file hw6.rkt. Both should be in a directory in your repository called hw6.

Homework Problems
For every function you write, you must write a contract, a purpose, and tests with check-* utilities, with the usual exception for functions that produce complex images, for which "eyeball tests" are appropriate. Write helper functions where they are needed. All functions need contracts, purposes and tests, even if they are "only" helper functions.

Problem 1
Sets are essential to a huge variety of algorithms. This problem considers the efficiency of certain set operations when sets are represented as unordered lists. Without loss of generality, we will consider sets of symbols (as opposed to some other kind of data). First, here is a data definition for symbol-set:
;; a symbol-set is a (listof symbol) containing no duplicate symbols

Here is a test for set membership under this data definition.


;; set-mem? : symbol symbol-set -> bool (define (set-mem? s ss) (cond [(empty? ss) false]
48 / 66

[else (or (symbol=? s (first ss)) (set-mem? s (rest ss)))]))

Write out a time cost analysis for set-mem?. Your cost model should count applications of symbol=?, which you may assume takes some fixed amount of time. Your conclusion should be that set-mem? runs either in constant, logarithmic , linear, n log n, quadratic , or exponential time. Here is set insertion, guarding against insertion of duplicates.
;; insert : symbol symbol-set -> symbol-set (define (insert s ss) (if (set-mem? s ss) ss (cons s ss)))

The cost of insert is simply the cost of set-mem?; you do not need to write out an analysis for this. Here is a function for building a set of symbols from a list of symbols. (You could think of this as a duplicate elimination function.)
;; list->sym-set : (listof symbol) -> symbol-set (define (list->sym-set ss) (cond [(empty? ss) empty] [else (insert (first ss) (list->sym-set (rest ss)))]))

Write out a time cost analysis for list->sym-set. Your cost model should count the cost of insert. Your conclusion should be that list>sym-set runs either in constant, logarithmic , linear, n log n, quadratic , or exponential time.

Problem 2
Piazza post 456 presented code for generic backtracking search. The following code is the same as the code at that post, with slight modifications (some comments have been added and some variable names changed).
;; is a type variable, a mnemonic for "state." ;; search : ( -> (or (listof ) 'goal)) -> (or false) ;; generic backtrack search starting from the start state. The next ;; function either returns 'goal, when its argument is a goal state, ;; '() when its argument is a dead end, or else a list of successor ;; states. (define (search start next) (local {;; look : -> (or false) ;; starting from given state, return a goal state or false (define (look s) (match (next s) ['goal s] [next-states (local {;; lp : (listof ) -> (or false) ;; look for a goal from list of states; ;; return first goal encountered, or false (define (lp ss) (cond
49 / 66

[(empty? ss) false] [else (match (look (first ss)) [#f (lp (rest ss))] [g g])]))} (lp next-states))]))} (look start)))

Post 456 subsequently shows how this generic search can be applied to searching binary trees, and also searching for a solution in the space of all possible n-queens solutions (for any n). Here are two more problems that can be solved by generic backtracking search, by defining, in each case, a state type, a value of that type representing the start state, and a next function to feed to the search function. Problem 2a The following coin puzzle comes from

http://www.puzzles.com/puzzleplayground/KeepItEven/KeepItEven.htm.

Given a four by four square arrangement of coins (see the figure), remove any six coins such that each row and column contains an even number of coins.

Define a type cp-state (for "coin-problem state") and a function cp-next of the appropriate type (examine the type of search above). Also, define a value cp-solution that calls search to find a solution to the problem. Note there are no successor states after six coins have been removed from the board; cp-next should behave accordingly. Problem 2b A 3x3 magic square is an arrangement of nine numbers such that the sum of each row, column and diagonal of numbers is the same. Here is a quasi-magic square populated with 1s: 1 1 1 1 1 1
50 / 66

Let the type ms-state (for "magic square state") be defined as follows:
;; an ms-state is a (make-ms-state placed to-place) where ;; - placed is a (listof (or num false)) of length 9, and ;; - to-place is a (listof num) of length 0 to 9

The list placed must include the value false in each empty square and numbers otherwise, and to-place is the list of numbers yet to be placed in the possible solution. The start state for the magic square consisting of all 1s is as follows:
(make-ms-state (make-list 9 false) (make-list 9 1))

Write ms-next, and define a value ms-solution-1-9 that calls search to build a 3x3 magic square containing the numbers from 1 to 9.

Problem 3
Write the following functions that operate on vectors. The function vec-max should return the maximum value in a non-empty vector of numbers.
;; vex-max : (vec num) -> num ;; return the max value of a vector of numbers

The function vec-reduce consumes a commutative operator, and identity, and a vector, and reduces the whole vector by that operator. The function is like a restricted fold. The fact that the operator is commutative is not enforced by the function; it's the caller's responsibility. You may reduce either left to right or right to left.
;; vec-reduce : ( -> ) (vec ) -> ;; reduce the vector given a commutative operator and an identity

The function vec-indices should consume a test and a vector, and return the indices of the items in the vector for which the test is true.
;; vec-indices : ( -> bool) (vec ) -> (listof num) ;; return the list of indices of values for which the test is true, ;; in ascending order

51 / 66

CMSC 15100: Project 1


Tues 10/29
CS151 Coursework | CS151 Home Project 1 will be collected from your subversion repository on Monday, November 4 at 11:59pm. Language: Intermediate Student with lambda
(require 2htdp/image) (require racket/match)

Please create a project directory in your subversion repository with


$ svn mkdir project

Work in the file project/proj1.rkt. It is important that this file has exactly this name and location; we will be looking for your work under this name at collection time. This week, you will build some of the foundational infrastructure of a program to play the game Gobblet Gobblers (presented in lectures on Monday Oct 28). Later in the quarter, you will write programs to play the game "intelligently" by selecting good moves, but before we get to that task, we need to establish what the game is: what data structures represent it, what its rules are, what constitutes a victory, etc.

Modeling the Game


Please copy the following data definitions into your proj1.rkt file. Comments follow.
;; a size is either 1, 2, or 3, ;; representing small, medium, large in that order ;; a player is either 'blue or 'orange ;; a square is a natural number between 0 and 8 inclusive ;; a piece is a (make-piece s p) where ;; - s is a size, and ;; - p is a player (define-struct piece (size player)) ;; an intro is a (make-intro p s) where ;; - p is a piece, and ;; - s is a square
52 / 66

(define-struct intro (piece square)) ;; a shift is a (make-shift src dst) where ;; - src and dst are both squares (define-struct shift (src dst)) ;; a move is either ;; - an intro, or ;; - a shift ;; an inventory is a (listof piece) ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; a board is a (listof (listof piece)) of length exactly 9 representing the squares of the board. Each square is represented by a list of pieces, where the pieces are ordered from outermost (i.e., biggest) to innermost (smallest). An square with no pieces is represented by the empty list. The order of the 9 items in the list corresponds to the squares on the board as follows: 0 1 2 3 4 5 6 7 8

;; a game is a (make-game next oinv binv board) where ;; - next is a player, ;; - oinv ("orange inventory") is an inventory, ;; - binv ("blue inventory") is an inventory, and ;; - board is a board (per definition above) (define-struct game (next oinv binv board))

Some comments about the game structure: The game struct represents the complete state of the game at a particular moment. Note that this includes more than just the board. At any moment in the game, each player has an inventory (those pieces not yet on the board), and there is a player designated to move next. In any functions that produce an image of the board (see below), the numbered squares must appear in the following layout (corresponding to their list order):
0 1 2 3 4 5 6 7 8

Game Logic
Working from the definitions above, implement the following functions that support the basic game logic:
;; new-game : player -> game

This function generates an initial game state, where both players have full inventories and the board has no pieces on it. The player argument to new-game is the player who is to move first. Each player starts with six pieces in their inventory: two of each size. Note that the fact that the board is initially empty does not mean it is initially empty.
53 / 66

;; pieces-at : board square -> (listof piece)

Return the list of (from zero to three) pieces at the given square.
;; pieces-valid? : game -> bool

Test that the collection of pieces in the game (on the board or in an inventory) includes exactly the pieces it should (that is, two of each size and six of both colors), and that each inventory contains only pieces of the right color.
;; squares-valid? : board -> bool

Test whether all squares on the board are in a legal state: that is, that there are zero to three pieces on each square, and that all gobbling is well-founded (every piece that gobbles another piece is strictly larger than what it gobbles). Note that (as stated above) the list of pieces at each square is in outer-to-inner order.
;; square-available? : piece square board -> bool

A square is available to a given piece if it is either empty, or non-empty but able to be gobbled by that piece.
;; move-legal? : move game -> bool

Test whether a given move is legal in a given game state: that the player to move actually possesses the piece in question (in the case of an intro), and that the destination square is available (per the previous function).
;; victory? : player game -> bool

Test whether the given player is victorious in the given game state.
;; apply-move : move game -> game

Apply the given move to the game and return the game's subsequent state. Note that an intro move has the effect of both adding a piece to the board and removing it from an inventory. If the proposed move is illegal, raise an error. Note that if the move is a shift that results in a "revealed" victory for the non-moving player, the next game state should be the victory for the non-moving player (since the game never reaches the state of the completed shift). In such a case, you can stash the would-be shifted piece back into its player's inventory in the game that results.

Visualizations
The second part of the assignment is to implement several functions for visualizing the state of the game.
;; board-image: board -> image
54 / 66

Draw view of the board from directly above "as a person would see it" in the orientation given (see above). That is to say, you should see only the outermost "gobbling" piece in each square. You do not need to draw little faces on the pieces, or felt tufts on their tops; blue and orange circles are adequate.
;; xray-board-image: board -> image

This should draw an image like the previous function, but you should be able to see all pieces in this view, not just the outermost ones (hence, xray). Concentric rings are adequate for this purpose.
;; game-image : bool game -> image

Visualize the entire game state, including a visualization of the two players' inventories and some indication of whose turn is next. The boolean argument to the function indicates true for an xray board image, false for the standard board image with opaque pieces. As long as you represent the whole game clearly in your image, you have some liberty designing it. However, if you prefer to follow a specification, here is one: Draw the board (in either opaque or xray mode, per the argument). Underneath the board, draw the orange inventory as a row of images of pieces (i.e., orange circles). Underneath that, draw blue's inventory the same way. Underneath that, use text to write either orange is next or blue is next accordingly.

Comments
You should write helper functions in abundance. Seek concise, clear solutions. If you find yourself copying and pasting large chunks of code, ask yourself whether there is a more elegant solution, possibly involving higher-order programming.

55 / 66

CMSC 15100: Project 2


Saturday 11/16
CS151 Coursework | CS151 Home Language: Intermediate Student with lambda
(require racket/match) (require 2htdp/image) (require 2htdp/universe)

Work in the file project/proj2.rkt. It is important that this file has exactly this name and location; we will be looking for your work under this name at collection time. Project 2 is due Monday, November 25 at 11:59pm.

Project 2 Seed Code


Look in your repositories, under the folder project, for the file proj2-seed.rkt. This file contains (slightly modified) Gobbler data definitions, game logic (along the lines of project 1), world verification code (also like project 1), and GUI code in the style of 2htdp/universe. This code is very close to complete, but we have left part of it to you. Part of the challenge of this project is to read and understand someone else's nontrivial code base (ours) such that you can extend it and complete it as needed. First, here are slightly modified Gobblers data definitions to build upon (the differences are called out in comments below):
;;;;;;;;;;;;;;;;;;;; PLAYER, MOVE, AND GAME DATA DEFINITIONS ;;;;;;;;;;;;;;;;;;;; ;; a (pair X Y) is a (make-pair x y), where ;; x is an X and y is a Y (define-struct pair (fst snd)) ;; a size is either 1, 2, or 3, ;; representing small, medium, large in that order ;; a player is either 'blue or 'orange ;; a square is a natural number between 0 and 8 inclusive ;; a piece is a (make-piece s p) where
56 / 66

;; - s is a size, and ;; - p is a player (define-struct piece (size player)) ;; an intro is a (make-intro p s) where ;; - p is a piece, and ;; - s is a square (define-struct intro (piece square)) ;; a shft is a (make-shft src dst) where ;; - src and dst are both squares ;; NOTE: the name of this data structure has changed ;; to avoid conflict with the name "shift" in universe (define-struct shft (src dst)) ;; a move is either ;; - an intro, or ;; - a shft ;; an inventory is a (listof piece) ordered by increasing size ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; a board is a (listof (listof piece)) representing a rectangular grid of squares. The playing board is a 3x3 grid, while the inventory boards are 3x2 grids. Each square is represented by a list of pieces, where the pieces are ordered from outermost (i.e., biggest) to innermost (smallest). An square with no pieces is represented by the empty list. The order of the squares in this list is row-major order; for example, the playing board squares are ordered as follows: 0 1 2 3 4 5 6 7 8

;; a game is a (make-game next inv board) where ;; - next is a player, ;; - inv is a pair (make-pair oinv binv), where ;; - oinv ("orange inventory") is an inventory, ;; - binv ("blue inventory") is an inventory, and ;; - board is a board (per definition above) ;; NOTE: the inventories are stored in a pair structure now; ;; this differs from the project 1 design (define-struct game (next inv board))

We have modified these data definitions as follows: what was called shift is now called shft (no i) because the former is already defined in universe, and the game now stores a pair of lists as an inventory, rather than two separate lists.

Game Logic
Implement the following two functions:
;; possible-moves : game -> (listof move)

If the game is in a victory state for one of the players, return the empty list. Otherwise, return a list of all possible legal moves starting from the current state of the game. Note this is not a list of all possible good moves, but rather all moves whether they are advantageous or not
57 / 66

(including shifts that cause an immediate loss). Except for victorious board positions, there are always moves possible in this game. There are many ways to prove this: here is one. The largest pieces (size 3 pieces) can be blocked on at most three squares, since there are only three other pieces in the game that can block them. Every such piece is either in the inventory or on the board. If the piece is in the inventory, there must be at least six squares (of the nine) available to it for an intro move. If the piece is already on the board, there must be at least five squares (of the other eight) available to it for a shift move.
;; random-move : game -> (or move false)

The function should return a randomly-selected legal move from among possible legal moves. It should do so with equal probability among its choices. Writing random-move follows immediately from having written possible-moves; simply select from among the list of possible moves at random. Use the built-in function ;; random : num -> num to do so. You do not have to prove that you actually achieve equal probability; you may rely on built-in random. If there are no legal moves, return false. Note the return type of random-move, with its application of or to two types, states that the return of this function is either a move or the value false (the sole inhabitant of an implied false type). Types of this sort (or false) appear throughout the Gobblers GUI code and are, in general, common. The predicate false? is built in to Racket and can be applied to any value (not just booleans).

Completing the GUI


Implement run-game with the following contract and purpose:
;; run-game : player (game -> move) -> world ;; given a first player and a move-choosing function, ;; call big-bang and run the application

The body of run-game is simply a call to big-bang with appropriate clauses to-draw, on-mouse, on-key, on-tick, check-with, and stop-when. What each of these clauses must contain is discussed in turn below. Since big-bang itself returns a world (it returns the world as it was when the universe came to an end), if you call big-bang inside run-game, you will return a value of the right type without doing anything else. The big-bang clauses are specified here:
to-draw

call the function that draws the world (provided)


on-mouse

call the function that handles mouse events (provided)


58 / 66

on-key

call a key-handling function that handles mouse events (not provided, discussed below)
on-tick

call the tick-handing function (provided, but discussed further below) with a tick interval of 1 second
check-with

call the function that checks if the world is valid (provided)


stop-when

test if the game play is done (no such function provided; just look for the 'quit state) You might also include name and state clauses as you prefer (see the universe documentation if interested). A word of warning: the performance of state is pretty awful in the presence of such an active mouse handler as ours. Please note the design of the provided tick-handling function:
;; tick-fn : (game -> move) -> (world -> world)

This is a curried function; given one argument of type game -> move, it returns a world -> world function suitable for use with on-tick. The argument it consumes is a move-choosing function, to be used by the computer on a tick event exactly when the world state is 'waiting-for-computer. The key handler must behave as follows: if the game is in a drag state, ignore all keystrokes. if the user strikes q, quit. if the user strikes r, reset the game. if the user strikes x, toggle xray mode. (The drawing will appear as an "xray" immediately, via our provided code, in lockstep with the relevant boolean value in the current world.) if the game has just ended on victory, and the user strikes y, start a new game. if the game has just ended on victory, and the user strikes n, quit. Other than that, keystrokes should have no effect.

Comments
Please note that writing the assigned game logic and GUI functions (though few in number) will entail writing many helper functions.
59 / 66

Testing available-moves is complicated because there are so many results, and furthermore the results need to be presented in the same order in any check-expects if you're doing list-to-list comparison. You can test things about the results, like how many results there are, or make sure that a given list of results includes a particular move or two. But the usual testing criteria don't apply. Use your judgment and be creative about testing it. Testing random-move is also complicated; you can just test it in Interactions and convince yourself it works. And testing run-game the check-expect way is impossible. The helper functions might be easy to test for each, though; test those the usual way whenever possible. Seek concise, clear solutions. If you find yourself copying and pasting large chunks of code, ask yourself whether there is a more elegant solution, possibly involving higher-order programming.

60 / 66

CMSC 15100: Project 3


Wednesday 11/27
CS151 Coursework | CS151 Home Language: Advanced Student
(require racket/match) (require 2htdp/image) (require 2htdp/universe)

Work in the file project/proj3.rkt. It is important that this file has exactly this name and location; we will be looking for your work under this name at collection time. Project 3 is due Tuesday, December 10 at 11:59pm.

Project 3
For project 2, you wrote
;; random-move : game -> (or move false)

The goal of project 3 is to write a program to make strategic rather than random choices, to allow you to build a competitive Gobblers player. You may start with the project 3 seed code that will have been added to your repository.

Game Strategy with Minimax


Minimax is an algorithm for game strategy. It depends on having some way to assign a numeric value to a game state. The following function assigns a number to each board position.
;; score : game -> num (define (score g) (cond [(victory? 'orange g) 1] [(victory? 'blue g) -1] [else 0]))

In automated game playing, functions like this are known as heuristic functions. This is as simple as a heuristic function can be, yet it is still useful. (More sophisticated heuristic functions have a greater variety of possible return values, not just identifying victories but also quantifying
61 / 66

the relative strength of intermediate positions as well.) In a zero-sum game like Gobblers, one player (in this case, orange) wants the board to achieve the maximum possible score, while the opposed player wants the minimum. The minimax algorithm assigns a number to a game state as follows. Call one player the max player, and the other the min player (in Gobblers, orange and blue respectively). If the game is finished, simply assign the game a score per the chosen heuristic function. If the game is not finished, consider all possible moves from the current game state, and recursively evaluate all the games that result from applying those moves. From among those, if it is the max player's (orange's) turn, choose the highest score, and if it is the min player's (blue's) turn, choose the lowest score. To choose a move, the automated player simply selects the most desirable move from among the possible moves that leads to the game state with the highest or lowest score (for the max player and min player, respectively). That is a simple statement of the minimax algorithm. For small games - those with few possible states (for example, tic-tac-toe) -- a program can enumerate the whole game in a few seconds, and proceed accordingly. For larger games, exploration of the whole game space is infeasible; one needs a way (or several) to limit the amount of computation that needs to be done. For Gobblers, we will constrain the minimax algorithm such that it looks ahead only a finite number of moves. A level of look-ahead is widely called the ply, as in, this program does a 3-ply search (meaning it looks 3 moves ahead). According to our Racket prototypes, a 2-ply Gobblers player plays quickly and reasonably well and with some apparent cleverness, although it makes obvious mistakes. A 3-ply player plays better than 2 and takes noticeably longer, and a 4-ply player plays very well (not perfectly) but takes a long time (dozens of seconds or more per move). For the first part of project 3, implement
;; minimax : nat game -> integer

to consume the ply (the number of moves to look ahead) and a game, and assign a score using the heuristic function above per the minimax algorithm. Then implement
;; minimax-choose-move : nat game -> move

to choose a move given a ply and a game. In testing minimax and minimax-choose-move, use some boards that have obvious features, such as winning boards (trivial, but make sure they work) and boards where one player is just about to win. minimax-choose-move should raise an error if asked to choose a move on a board where one player has already won.
62 / 66

Having written minimax-choose-move, you will have a player that you can plug into the Gobblers project infrastructure and play against, and, depending on the ply you choose, should be able to beat you some or almost all of the time.

Transposition Tables
Once you have computed the strategy for a given position in Gobblers, there is no reason to compute it again. We will use what is called a transposition table to store the positions already considered. In other words, transposition tables give the program a way to remember positions it has seen before. We represent the transposition table as a vector of tt-entry structs.
;; a tt-entry is a (make-tt-entry hash score depth) ;; where hash, score and depth are numbers (define-struct tt-entry (hash score depth))

In each transposition table entry, the hash is a 55-bit number representing the state of the board. The format of these numbers is discussed below (see "Hashing Games"); it is unique per game state. The score is per the heuristic function above, and the depth is the ply at which that score was calculated. Near the top of the source code, create a globally visible transposition table as follows:
(define TT-size 100003) ;; a prime number (define TT (make-vector TT-size empty))

You will read from and write to this table as you play multiple games; over time (at least within one session of game play) it will encode more and more knowledge about Gobblers, and become a faster and better player. Note that choosing a prime number for the size of the table (a typical tactic in hash table construction) helps spread out the table entries across buckets. You may experiment with different values for TT-size in your development. To supplement the minimax algorithm with a transposition table, implement
;; minimax-tt : nat game -> number

and
;; minimax-tt-choose-move : nat game -> move

For minimax-tt, in the cases where the return is not immediate (i.e., completed games or games where there the number of moves to look ahead has run out), the function should first attempt to find a transposition table entry for the current game. If it finds no entry, it should recursively compute the value of the
63 / 66

current game state and use that As a side effect it should store the score it computed in the transposition table for future reference. If it finds an entry, and if that entry reflects the current ply or greater, or if the entry reflects a definite win for either player (i.e., is non-zero), it should use the score in that entry. If it finds an entry and the entry reflects a shallower ply, it should recursively compute the value of the current game state and, for future reference, store the new, deeper-ply information in the transposition table. To compute the transposition table's bucket for any particular game state (for either reads or writes), find the remainder of the hash code when divided by the table size (use the modulo function). To find an entry in a bucket, look for the desired hash code in the list of tt-entry structs there. is very similar to minimax-choose-move, but it needs to call minimax-tt rather than minimax.
minimax-tt-choose-move

Hashing Games
Each square in the game can be represented by a string of six bits as follows. We assign (arbitrarily) a meaning to each bit in a six-bit bitstring, from left to right: the first bit indicates presence (1) or absence (0) of piece O3 (for orange size 3), the second bit presence or absence of B3, and so on for O2, B2, O1, B1. In this way the square with O3, B2 and B1 on it is represented as 100101, the square with only B2 on it 000100, and the empty square 000000. This method encodes all possible squares and furthermore assigns a unique bitstring to each configuration. Note that certain bitstrings do not correspond to legal square configurations (e.g., 110000). This is fine; we simply cannot encounter those bitstrings in (well-founded) games. By interpreting each bitstring as a binary number, and since each bitstring is distinct, we can therefore assign a unique six-bit number between 0 and (2^6-1)=63 to each possible square. Let c 0 be the bitstring encoding of square 0, c 1 for square 1, and so on up to c 8 . We construct a bitstring for the whole game state by writing s sixbit square encoding in order from 0 to 8, then finally one more bit, 0 for orange's turn, 1 for blue's turn. As such, if it is orange's turn and the board looks like this:

(that's B3 and O2) the corresponding bitstring (with spaces added for
64 / 66

clarity only)
000000 000000 010000 000000 000000 001000 000000 000000 000000 0

Treating these bits as a number, we have 2199027449856 as the hash code for that game state. Compute the hash code for a given game state as follows. First, compute the number between 0 and 63 corresponding to each square; let's call those c 0 , c 1 , etc. as above. Then the hash code is c 0 249 + c 1 243 + c 2 237 + c 3 231 + c 4 225 + c 5 219 + c 6 213 + c 7 27 + c 8 21 + t

where t is 0 for orange's turn, 1 for blue's. Note that the square parts equation can also be computed using a properly-designed foldl over the list of squares.

Exploiting Symmetry
As an optional extension of this project, exploit symmetries in the transposition table. Each board has many equivalent boards, which was the subject of lab 7. Since every board in an equivalence class has the same strategic value as every other board, looking up any one of those items in the equivalence class is the same as looking up any other. Furthermore, the scores in the transposition table are the negations of the scores for the inverted board's equivalence class. Modify your transposition table implementation to exploit strategic symmetries on reading from the transposition table. To exploit symmetries on read, for game g, if g is not found in the table, look for every equivalent board g' in the table as well (and possibly every inverted equivalent g''). To help with this, we suggest you write a version of your hash function to return a list of hash keys (for a game's whole equivalence class).

Comments
From the point of view of entertainment, it is desirable to write a computer player with some unpredictability in it; that is, a player that chooses a move at random from among equivalently good choices. You will find this to be an undesirable feature during development, however, since you will want to be able to replay games with problems and/or unexpected features (and not have to luck into them). Therefore, while you might want to build in some randomness before you show this game to friends and family for fun, during development make sure you write deterministic algorithms for choosing moves.
65 / 66

66 / 66

Вам также может понравиться