Harvey Mudd College
Computer Science 42
Assignment 1, Due Monday, September 6, by 11:59pm

A Computational Racket and Picobot!

This homework provides an introduction to a side of computer science you've probably never seen before.  In part 1 you will work with a simple robot called Picobot.  Picobot is patterned after the iRobot Roomba and its goal is much the same: to fully cover (vaccuum) its environment.  Part 2 will introduce you to a language called Racket, which is a descendent of the language Scheme.  Racket (and Scheme) is considered a very "clean" language, with a minimal syntax: few punctuation and formatting rules. In fact some might argue it's too minimal! As a result, Racket is one of the best examples of a language whose focus is supporting computation itself, as opposed to intricate data structuring.  In this assignment you'll implement some useful and some silly but fun functions to familiarize yourself with Racket and to re-familiarize yourself with recursion.

Pair and individual problems

Every week there will be a set of problems that must be completed individually and a set of problems on which you may work with a partner if you want (or you may complete these problems individually).  On a given week, either set might be empty, but we will make it clear in each assignment which problems are which.  Each problem below is marked either "individual or pair" or "individual".  Please remember that pair programming problems are subject to the pair programming guidelines found in the syllabus.

Submitting your work

For this assignment, you should type (and test!) your programs in four files

 hw1pr1.txt -- AUTOMATICALLY CREATED BY THE PICOBOT PAGE ON MAP 1 (empty room)

 hw1pr2.txt -- AUTOMATICALLY CREATED BY THE PICOBOT PAGE ON MAP 2 (maze)
 hw1pr3.rkt
  hw1pr4.rkt

You should submit problems 1 and 2 directly from the picobot site.  You should submit problem 3 and 4 from the submissions site.  If you work with a partner on any part, one partner will submit the file, and the other must acknowledge the submission.

To log in to the site, use your login id, which should be your first initial followed by your last name. Use the default password at first. After you log in the first time, be sure to change it to something you'll remember. Note that the site may ask you to re-login with your old password before asking for the new one.   The only way to log out is to close your browser.  BE CAREFUL IF YOU ARE USING SOMEONE ELSE'S COMPUTER THAT YOU RESTART THE BROWSER BEFORE SUBMITTING YOUR FILES!!

You may submit your work multiple times -- only the file submitted latest (before the deadline) will be graded, but all of them will be saved in case you or we need to access them for any reason. The submission site is a convenient way to access your latest copy of a HW assignment if you work on multiple computers.

Design, commenting, and formatting

Design and formatting account for roughly a quarter of the credit for each function or program you complete.

Design, here, means algorithm design, that is, how you break a problem down into smaller pieces and then compose those pieces into an overall solution. Large, convoluted functions are difficult to understand (and debug!). Use small, clear functions with lots of small helper functions, as necessary.

Formatting includes coding style: clean and clear structure, meaningful variable and function names, ample use of spacing, and indenting that reflects code structure.

As for commenting, in each problem we ask you to include

Below is an example -- feel free to copy this template and use it!

hw1pr3.rkt, with comment and testing templates

To run this code, you will need to set up Dr. Racket and grab our tester.scm file.  See problems 3 and 4, below.

Important warning about function names!    Be sure to name your functions as indicated in the problem statements below -- including capitalization and punctuation! This helps our graders, who can automate the running of many tests using those function names. Helper functions may have any name you like, but they should be descriptive and appropriate. When graders have to manually change your function names to match the ones in the homework problem descriptions, it angers them -- and a key guideline for CS 42 is not to anger the graders!

Testing your code

Computation in the form of software is useful to the extent that it works. But it only works to the extent that it has been tested! Testing your own software thoroughly is important -- we use test cases to check how well your functions work, and you will certainly want to do the same!

Some test cases are indicated for each of the problems. These can be copied from this assignment site into your code. When you use auxiliary functions, it is good practice (and will save time in the long run) to include tests for them as well.  A small but non-trivial part of your grade each week may be based on how well you test your programs, so you should create at least one (more where appropriate) additional test for the primary functions, too!

 Picobot: Problems 1 and 2

The first two problems explore a simple "robot," named "picobot," whose goal is to completely traverse its environment.

Here is a link to the Picobot page. (Works only in NON-IE Browsers)

Picobot starts at a random location in a room -- you don't have control over Picobot's initial location. The walls of the room are blue; picobot is green, and the empty area is white. Each time picobot takes a step, it leaves a grey trail behind it. When Picobot has completely explored its environment, it stops automatically.

Surroundings

Not surprisingly, picobot has limited sensing power. It can only sense its surroundings immediately to the north, east, west, and south of it. For example,

In the above image, Picobot sees a wall to the north and west and it sees nothing to the west or south. This set of surroundings would be represented as follows:

NxWx

The four squares surrounding picobot are always considered in NEWS order: an x represents empty space, the appropriate direction letter (N, E, W, and S) represents a wall blocking that direction. Here are all of the possible picobot surroundings:

State

Picobot's memory is also limited. In fact, it has only a single value from 0 to 99 available to it. This number is called picobot's state. In general, "state" refers to the relevant context in which computation takes place. Here, you might think of each "state" as one piece -- or behavior -- that the robot uses to achieve its overall goal.

Picobot always begins in state 0.

The state and the surroundings are all the information that picobot has available to make its decisions!

Rules

Picobot moves according to a set of rules of the form

StateNow   Surroundings   ->   MoveDirection   NewState

For example,

  0   xxxS   ->   N   0  

is a rule that says "if picobot starts in state 0 and sees the surroundings xxxS, it should move North and stay in state 0."

The MoveDirection can be N, E, W, S, or X, representing the direction to move or, in the case of X, the choice not to move at all.

If this were picobot's only rule and if picobot began (in state 0) at the bottom of an empty room, it would move up (north) one square and stay in state 0. However, picobot would not move any further, because its surroundings would have changed to xxxx, which does not match the rule above.

Wildcards

The asterisk * can be used inside surroundings to mean "I don't care whether there is a wall or not in that position." For example, xE** means "there is no wall North, there is a wall to the East, and there may or may not be a wall to the West or South."

As an example, the rule

  0   x***   ->   N   0  

is a rule that says "if picobot starts in state 0 and sees any surroundings without a wall to the North, it should move North and stay in state 0."

If this new version (with wildcard asterisks) were picobot's only rule and if picobot began (in state 0) at the bottom of an empty room, it would first see surroundings xxxS. These match the above rule, so picobot would move North and stay in state 0. Then, its surroundings would be xxxx. These also match the above rule, so picobot would again move North and stay in state 0. In fact, this process would continue until it hit the "top" of the room, when the surroundings Nxxx no longer match the above rule.

Comments

Anything after the pound sign (#) on a line is a comment (as in python). Comments are human-readable explanations of what is going on, but ignored by picobot. Blank lines are ignored as well.

An example

Consider the following set of rules:

# state 0 goes N as far as possible
0 x*** -> N 0 # if there's nothing to the N, go N
0 N*** -> X 1 # if N is blocked, switch to state 1

# state 1 goes S as far as possible
1 ***x -> S 1 # if there's nothing to the S, go S
1 ***S -> X 0 # otherwise, switch to state 0

Recall that picobot always starts in state 0. Picobot now consults the rules from top to bottom until it finds the first rule that applies. It uses that rule to make its move and enter its next state. It then starts all over again, looking at the rules and finding the first one from the top that applies.

In this case, picobot will follow the first rule up to the "top" of its environment, moving north and staying in state 0 the whole time. Eventually, it encounters a wall to its north. At this point, the topmost rule no longer applies. However, the next rule "0 N*** -> X 1" does apply now! So, picobot uses this rule which causes it to stay put (due to the "X") and switch to state 1. Now that it is in state 1, neither of the first two rules will apply. Picobot follows state 1's rules, which guide it back to the "bottom" of its environment. And so it continues... .

Picobot problems

For this part of the assignment, your task is to design two different sets of picobot rules:


Remember that your solutions must work from arbitrary starting positions within the environment.

IMPORTANT Note: There are many different maps included on the picobot site.  For this assignment, you only need to deal with map 1 (the empty room) and map 2 (the maze).  The map you are on will AUTOMATICALLY determine which problem you are submitting to, so make sure you submit the right rules with the right map.

The rest of the maps are purely for your fun, and you should not submit rules for these maps.

Optional extra credit for problems 1 and 2

At heart, CS fundamentally tries to answer questions of complexity: to show that problems are easier than initially thought -- or, sometimes, to prove that they can't be handled with fewer resources.

You might think about how efficient your solutions are -- both in terms of the number of states used and in terms of the number of rules. There are other ways to measure efficiency as well (e.g., speed).

For optional extra credit, try to create as efficient a solution as possible for both the empty room and the maze-solving set of rules. That is, strive to use as few rules as possible in creating your solution - so long as your solution will still succeed from any starting point in the environment! Up to +10% extra credit for each problem is available for solutions that match - or beat - 7 rules in the empty room and 8 rules for the maze-solver.'

If you just want to play around more, you can also try to write rules to cover the additional environments that you find on the picobot site.  These aren't worth any credit, but they are worth a lot of fun!  :)

Racket: Problems 3 and 4

Obtain access to the Dr. Racket programming environment, either by downloading it to your personal computer or by using the CS lab. On the lab Macs, Dr. Racket is located as follows:

If you have forgotten your CS login or password (they may be different than the CIS one), feel free to ask Tim Buchheim, our administrator across the hall in Beckman B101, to reset it. I'm happy to help with this, too.   Note this is not the same as your submissions site login.

Once you've started Scheme, go to the "Language" menu and select "Choose language..." Then, expand the "PLT" arrow to find the "Pretty Big" subset of Scheme. Different versions of Dr. Scheme offer the "Pretty Big" subset in different places. Choose this "Pretty Big" subset as the language for this (and subsequent) assignments.

There are also many pointers to Racket support on the references page.

A good starting point for writing the functions in this assignment is to download the hw1pr3.rkt file, linked both here and above, which provides a template and example of a function. Also, download the tester.rkt file, which provides the test and tester definitions. Keep both files in the same directory. Examples of commenting and code-testing are already in hw1pr3.rkt.

Problem 3 (30 points) [INDIVIDUAL], submit these functions in a file named hw1pr3.rkt

Remember that for each problem below, you should include at least one additional test in your file, beyond what we provide.  Please be sure these additional tests are clearly marked with comments.  For example, in problem 3.1 you might have in your code:

; Provided tests
(test (erdos 3) 10)
(test (erdos 10) 5)

; Additional test
(test (erdos 4) 2)

Problem 3.1:    erdos

For this problem define a function, named erdos, with one argument, N. Thus, your definition will begin as follows:

(define (erdos N)
This function should compute the following:

Here are a couple of tests to try.
(test (erdos 3) 10)
(test (erdos 10) 5)
Iterating this simple function leads to surprisingly difficult mathematical challenges, as this link (and the next function) suggests. The name erdos is from the mathematician Paul Erdos and is (very roughly) pronounced air-dish.

Problem 3.2:    erdosCount

Write erdosCount, a function of one postitive integer argument:

(define (erdosCount N)
where erdosCount should return the smallest number of times erdos must be iterated, when started with an input of N, in order to arrive at a value of 1. Let's use the symbol ==> to mean "evaluates to." Then, (erdosCount 3) ==> 7, because
(erdos (erdos (erdos (erdos (erdos (erdos (erdos 3))))))) ==> 1
and there are 7 applications of erdos above. Here are two more tests:
(test (erdosCount 26) 10)
(test (erdosCount 27) 111)
Be sure to use recursion when writing erdosCount. As a starting point, consider how we wrote length in class.

Aside    It is an open question in mathematics whether or not (erdosCount N) halts for every positive integer N. It does halt for all of the values of N for which it has been tried; it is conjectured always to halt, but no one has proven it one way or the other. Paul Erdos famously quipped that "mathematics is not ready for such problems." The fact that a general-purpose "haltchecking" program would be powerful enough to answer this century-old conjecture hints at how strong a capability "haltchecking" actually is... . The state-of-the-art understanding of this 3x + 1 problem is available at this link.


Problem 3.3:    removeAll

In class, you saw how (remove e L) works: it returns a list identical to L, except with the first top-level instance of e removed. For this problem, write (removeAll e L), which should return a list identical to L, except that all top-level instances of e have been removed. For example,

(test (removeAll "i" '("a" "l" "i" "i" "i" "e" "n")) '("a" "l" "e" "n"))
(test (removeAll "i" '( ("a" "l" "i") "i" "i" "e" "n" )) '(("a" "l" "i") "e" "n"))
(test (removeAll 0 '(1 0 1 0 1 0)) '(1 1 1))

Problem 3.4:    find

Write a function (find L M), taking in two lists L and M, such that find returns true (#t in Scheme) if L is a sublist of M. find should return false (#f) otherwise.

Note that the empty list is a valid sublist of any list at all. On the other hand, no non-empty list is a sublist of the empty list. Also, because find deals with sublists, not subsequences, all of the top-level elements of L must appear consecutively and contiguously at the top level of M in order to yield #t. For example,

(test (find '(1 2 3) '(0 1 2 3 4)) #t)
(test (find '(1 2 3) '(0 1 2 2 3 4)) #f) ;; they're not contiguous
Here are a few other tests -- you should devise a few of your own, as well!
(test (find '() '(1 2 3)) #t)
(test (find '(1) '()) #f)
(test (find '(1 2) '(2 1 1 1)) #f)
(test (find '(1 2) '(2 1 1 (1 2))) #f) ;; an element is not a sub-list
(test (find '(1 1 3) '(2 1 1 2 3 1)) #f)
Hint It may be helpful to use a second, helper function to handle the contiguous-element checking here... .

Problem 4 (40 points) [PAIR OR INDIVIDUAL]  Submit these functions in a file named hw1pr4.rkt

Remember that for each problem below, you should include at least one additional test in your file, beyond what we provide.  Please be sure these additional tests are clearly marked with comments.

Problem 4.1:    superreverse

Write a variant of reverse named (superreverse L), whose input L will be a list that may contain lists as some of its elements. superreverse should output a list identical to L, except that all of its top-level lists should be reversed. For example,

(test (superreverse '( (1 2 3) (4 5 6) 42 ("k" "o" "o" "l") ("a" "m") ))
'( (3 2 1) (6 5 4) 42 ("l" "o" "o" "k") ("m" "a") ) )
(test (superreverse '( (1 2 3) (4 5 6 (7 8) 9 ) ))
'( (3 2 1) (9 (7 8) 6 5 4) ) )

Problem 4.2:    duperreverse

Next define (duperreverse L), which returns a list whose structure is a complete reversal of L's. For example,

(test (duperreverse '( (1 2 3) (4 5 6) 42 ("k" "o" "o" "l") ("a" "m") ))
'( ("m" "a") ("l" "o" "o" "k") 42 (6 5 4) (3 2 1) ) )
(test (duperreverse '( (1 2 3) (4 5 6 (7 8) 9 ) ))
'( (9 (8 7) 6 5 4) (3 2 1) ) )
Scheme's built-in function (list? x) returns #t if and only if x is a list; otherwise it returns #f. For the purposes of this problem, consider any data type that is not a list to be atomic, that is, it can not be broken down to be reversed.

Hint: consider the flatten function we wrote in class! The differences among cons, append, and list will be important.

The next four problems build off each other to create a sort of automatic scrabble player

Problem 4.3:    score-letter

Association lists, or "a-lists" for short, are always lists of lists. For example, the above function, number-each outputs association lists. they are often used serve as data repositories, as this problem illustrates using the example of scrabble-letter scores.

The function (assoc e A) takes in an a-list A (remember that A contains lists of lists) and returns the element of A whose own first element is equal to e. For example:
 (assoc 3 '((1 jan) (2 feb) (3 mar) (4 apr))) ==> (3 mar)

(assoc 5 '((1 jan) (2 feb) (3 mar) (4 apr))) ==> #f
As the second example shows, assoc returns #f when e does not appear as a first element of any of A's sublists.

In this problem we want to use an association-list to score information about scrabble tiles. The following Scheme statement defines an A-list that provides all of the letter scores. You should copy this code into your hw1part2.scm file:

;; scrabble-tile-bag 
;;
;; letter tile scores and counts from the game of Scrabble
;; the counts aren't needed (but don't hurt)
;; obtained from http://en.wikipedia.org/wiki/Image:Scrabble_tiles_en.jpg
;;
(define scrabble-tile-bag
'((#\a 1 9) (#\b 3 2) (#\c 3 2) (#\d 2 4) (#\e 1 12)
(#\f 4 2) (#\g 2 3) (#\h 4 2) (#\i 1 9) (#\j 8 1)
(#\k 5 1) (#\l 1 4) (#\m 3 2) (#\n 1 6) (#\o 1 8)
(#\p 3 2) (#\q 10 1)(#\r 1 6) (#\s 1 4) (#\t 1 6)
(#\u 1 4) (#\v 4 2) (#\w 4 2) (#\x 8 1) (#\y 4 2)
(#\z 10 1) (#\_ 0 2)) ) ;; end define scrabble-tile-bag
;;
;; The underscore _ represents a blank tile.
;; The notation #\a is Scheme's unusual character notation
The notation #\a is Racket's way of representing single-character literals.

Construct a function named score-letter that determines the score for its argument, which will be a character. Use assoc. Some tests that should work include,
(test (score-letter #\w) 4)
(test (score-letter '#\z) 10)
(test (score-letter '_) 0)

Problem 4.4 score-word

Next, construct a function score-word that determines the score for a string of letters. Note that strings in Scheme are not the same as symbols, nor are characters the same as strings (as in Python). Strings are doubly quoted. The built-in functions string->list and list->string, which convert from strings to lists of characters and vice-versa, may be of use. Here are examples of them in action:

(string->list "hi") ==> (#\h #\i)
(list->string '(\#h \#i) ) ==> "hi"
Here are three tests of score-word:
(test (score-word "zzz") 30)
(test (score-word "fortytwo") 17)
(test (score-word "twelve") 12)

Problem 4.5  highest-word-score


Construct a function highest-word-score that finds the highest-scoring word in a list of symbols,
returning both the word and the score as a list of two elements. This may require
the invention of some helper functions. For the case that the list of symbols is empty,
return the following dummy value: '(_ 0)

The built-in functions symbol->string, which convert from symbols to strings, may be of use (there's also string->symbol).

Here are some test cases:

(test (highest-word-score '(two words)) '(words 9))
(test (highest-word-score '(one_word))  '(one_word 11))
(test (highest-word-score '(the quick brown fox just plays with my very lazy dog))
      '(quick 20))
(test (highest-word-score
      '(now is the time for each good person to come to the aid of his or her party))
      '(party 10))
(test (highest-word-score '()) '(_ 0))

Problem 4.6   best-word

Define a function best-word as follows:

(define (best-word rack wordList)
best-word takes two inputs: a string of letters rack and a list of legal strings named wordList. Then, best-word should return a list of two elements: the return value's first element should be the highest-scoring word from wordList that can be made from the letters in rack. The return value's second element should be the score of that highest-scoring word. If there is a tie, any one of the strings in the wordList with maximal score may be returned. When we test your code, we will make sure that the highest-scoring word in each test case is unique.

Here are some test cases:,
(test (best-word 'academy '(ace ade cad cay day)) '(cay 8)) 

(test (best-word 'apple '(peal peel ape pape)) '(pape 8))

(test (best-word 'pale '(peal peel ape pape)) '(peal 6))
Note that "paper" could not be made in the third example, because the rack had only a single #\p.

Hint: planning out your strategy for this problem before diving in to the coding is a good thing! In particular, you will want to define and use a number of helper-functions to keep your definition of best-word simple and elegant. You'll want to keep the helper-functions short, as well!