Functions operating on two lists - scheme

I am working on a Racket program for a class and I am totally stumped as to
how to implement one of the features.
The program uses Big-Bang and is supposed to implement a simple Space Invaders game.
I have everything working except one piece, and that is - how to handle the case
when a missile collides with an invader. The reason I'm struggling is that I don't
know how to write a function where I have two lists of arbitrary size, and I have
to check the fields of each object in one list with each object in another list and
remove an object in each list if they have the same values.
The world state is the game:
(define-struct game (invaders missiles tank))
where invaders and missiles are
both lists.
To produce the next state of the game, I implement a function called 'tock'.
Normally, I would just do:
(define (tock s)
(make-game (next-invaders (game-invaders s))
(next-missiles (game-missiles s))
(next-tank (game-tank s)))
But since the contents of the invaders and missiles lists might impact each other due to a collision, I can't simply update the positions independently and move on, I have to remove any collisions and then update the positions.
So I've tried:
(define (tock s)
(make-game (check-collision (game-invaders s)
(game-missiles s)
(game-tank s))
But this makes check-collision take a tank, which it doesn't need.
(define (tock s)
(make-game (next-invaders (game-invaders s) (game-missiles s))
(next-missiles (game-missiles s) (game-invaders s))
(next-tank (game-tank s))))
In this version, I have a function called next-invaders which takes the list of invaders and missiles, and a function
called next-missiles which takes the list of missiles and invaders. The first function checks each invader against each missile, attempts to remove any collided invaders and returns the remaining invaders. The second function checks each missile against each invader and attempts to remove any collided missiles and returns the remaining missiles. The answers should be the same, but it's duplicate work and I'm worried about a possible race condition. I don't know how else to construct a single expression where one function only needs two fields and the other one needs three
and I still wind up producing the next state of the game.
Here's an example of next-invaders. If there are no invaders, it does nothing. If there are invaders but no missiles,
it just moves each invader (move-invader) and recursively calls itself to iterate through all invaders. If there
are both missiles and invaders, then I check for a collision between the first invader in the list, and every
missile in the list; so check collision is recursive.
(define (next-invaders loi lom)
(cond [(empty? loi) empty]
[(empty? lom) (move-invader (first loi) (next-invaders (rest loi) lom))]
[(check_collision (first loi) lom)
(next-invaders (cons (rest loi) empty) lom)]
[else
(move-invader (first loi)
(next-invaders (rest loi) lom))]))
Is the 'answer' to check-collision the correct way to "remove" the collided invader from the list of invaders?
(define (check_collision i lom)
(cond [(empty? lom) false]
[(and (<= (- (missile-x (first lom)) (invader-x i)) HIT-RANGE)
(<= (- (missile-y (first lom)) (invader-y i)) HIT-RANGE))
true]
[else (check_collision i (rest lom))]))
Is this the correct way to test each element of each list against one another?
Update: Still going in circles on this problem. check-collision works and invader-function works, but when I return to missile-function, I don't know how to indicate that a missile needs to be deleted in the case where there was a collision detected in invader-function.
(define-struct invader (x y dx))
;; Invader is (make-invader Number Number Number)
;; interp. the invader is at (x, y) in screen coordinates
;; the invader along x by dx pixels per clock tick
(define-struct missile (x y))
;; Missile is (make-missile Number Number)
;; interp. the missile's location is x y in screen coordinates
(define-struct collision (invaders missiles))
(define (tock s)
(make-game (handle-invaders (collision-invaders (next-invaders-and-missiles (make-collision (game-invaders s) (game-missiles s)))))
(handle-missiles (collision-missiles (next-invaders-and-missiles (make-collision (game-invaders s) (game-missiles s)))))
(handle-tank (game-tank s))))
(define (next-invaders-and-missiles c)
(cond [(and (empty? (collision-invaders c)) (empty? (collision-missiles c))) (make-collision empty empty)]
[(or (empty? (collision-invaders c)) (empty? (collision-missiles c))) (make-collision (collision-invaders c) (collision-missiles c))]
[else
(missile-function (make-collision (collision-invaders c) (collision-missiles c)))]))
;; Collision -> list Of Missiles
;; produce an updated listOf Missiles taking collisions into account
(define (missile-function c)
(cond [(empty? (collision-missiles c)) (make-collision (collision-invaders c) empty)]
[else
(if (< (length (invader-function (first (collision-missiles c)) (collision-invaders c))) (length (collision-invaders c)))
(make-collision (collision-invaders c) (remove (first (collision-missiles c)) (collision-missiles c)))
(missile-function (make-collision (collision-invaders c) (rest (collision-missiles c)))))]))
;; Missile, listOf Invaders -> listOf Invaders
;; produce an updated listOf Invaders taking collisions into account
(define (invader-function m loi)
(cond [(empty? loi) empty]
[else
(if (check-collision? (first loi) m)
(remove (first loi) loi)
(invader-function m (rest loi)))]))
;; Invader, Missile -> Boolean
;; produce true if the coordinates of a missile are within HIT-RANGE of the coordinates of an invader
(define (check-collision? i m)
(and (<= (- (missile-x m) (invader-x i)) HIT-RANGE) (<= (- (missile-y m) (invader-y i)) HIT-RANGE)))

I haven't reviewed all the code, but the general solution is to have one function that takes the lists of missiles and invaders, checks for all the collisions, and then returns both updated lists by returning a pair of lists. So something like this:
(define (tock s)
(let* [(next (next-invaders-and-missiles (game-invaders s) (game-missiles s)))
(next-invaders (first next))
(next-missiles (rest next))]
(make-game next-invaders next-missiles (game-tank s))))
(define (next-invaders-and-missiles loi lom)
... ;; code that finds collisions and removes them from both lists
(cons new-loi new-lom))

Related

car: contract violation expected: pair? given: () during remove in binary search tree

I don't understand how I am getting a contract violation. It seems when I create a bst it doesnt make 2 empty lists it just has ().
This is my remove method:
;Returns the binary search tree representing bst after removing x where f and g are predicates as defined in bst-contains.
(define (bst-remove bst f g x)
;if empty return empty
(cond ((empty? bst) (bst-create-empty)))
;else if equal then check right if right is empty then pull from left
(cond ((g (car bst) x) (cond ((empty? (caddr bst)) (cond ((empty? (cadr bst)) (bst-create-empty))
(else (car(cadr bst)))))
;if right isnt empty then remove from left
(else(bst-create (bst-max-right (caddr bst)) (cadr bst) (bst-remove (caddr bst) f g (bst-max-right (caddr bst)))))))
(else (bst-create (car bst) (bst-remove (cadr bst) f g x) (bst-remove (caddr bst) f g x)))))
My bst-create and bst-create-empty:
;Returns an empty binary search tree.
(define (bst-create-empty)
'())
;Returns a binary search tree having the specified root value, with left-subtree left and right-subtree right.
(define (bst-create root left right)
(list root left right))
The code I give it is
(bst-remove (bst-create 5 (bst-create 6 (bst-create-empty) (bst-create-empty)) (bst-create 4 (bst-create-empty) (bst-create-empty))) < = 6)
The error i get is car: contract violation expected: pair? given: ()
You have got Scheme and especially cond all wrong. If you in a body of a procedure have two statements like:
(define (test lst)
first-expression
tail-expression)
It is obvious that the tail-expression will follow the evaluation and discarding of any result of the first-expression. Also unless first-expression has side effects it is dead code. Your cond expressions (cond ((empty? bst) (bst-create-empty))) is dead code since no matter what the outcome is it will never be a part of the result since Scheme will evaluate the second cond unconditionally. It does (car bst) which throws the error.
The correct way to have multiple returns are by one expression:
(cond
(test1 consequent1)
(test2 consequent2)
(test3 consequent3)
(else alternative))
Needless to say all the previous tests are negative so if test3 is true, then you know that test1 and test2 both had negative results. You also know that if consequent1 is evaluated no other terms or tests gets evaluated. It stops at the first prositive.
In you specific case the code could have looked like this:
(define (bst-remove bst f g x)
(cond ((empty? bst)
(bst-create-empty))
((not (g (car bst) x))
(bst-create (car bst) (bst-remove (cadr bst) f g x) (bst-remove (caddr bst) f g x)))
((not (empty? (caddr bst)))
(bst-create (bst-max-right (caddr bst)) (cadr bst) (bst-remove (caddr bst) f g (bst-max-right (caddr bst)))))
((empty? (cadr bst))
(bst-create-empty))
(else
(caadr bst))))
Using nested if works too, but it makes harder to read code just like your nested cond. Notice that I negated some of the tests since they only have one alternative but several tests in its consequent. By negating I could have one consequent and continue testing for the other cases in the same cond.
You refer to your second cond as an else if in your comment, but it's not an else-if, it's just an if. That is, you check the second condition even if the first one was true, in which case the car part of the condition causes this error.
To fix this, you should make both conditions part of a single cond, in which case it will actually act like an else if.
It looks like you've misunderstood cond, or possibly Scheme in general.
The result of a function application is the value of the last expression in the function's body (the only reason for having more than one is if you're doing something that has a side effect), and cond expressions are evaluated in exactly the same way as other Scheme expressions.
So the expression (cond ((empty? bst) (bst-create-empty))) does not return an empty tree, it is evaluated and produces an empty tree, and that tree is discarded.
Then evaluation continues with the next cond, which is a bad idea when the tree is empty.
A further problem is that the function should produce a tree, but (car (cadr bst)) wouldn't.
If you define a few useful accessor functions:
(define bst-value car)
(define bst-left cadr)
(define bst-right caddr)
then these lines are more obviously wrong:
(cond ((empty? (bst-left bst)) (bst-create-empty))
(else (bst-value (bst-left bst)))))
Fixing it, it becomes (reasonably) clear that the entire expression
(cond ((empty? (bst-left bst)) (bst-create-empty))
(else (bst-left bst))))
is equivalent to
(bst-left bst)
Now you have
(cond ((empty? (bst-right bst)) (bst-left bst))
( else make a tree...
But there's a lack of symmetry here; surely, if the left subtree is empty, the result should be the entire right subtree in a similar way.
So,
(cond ((empty? (bst-right bst)) (bst-left bst))
(empty? (bst-left bst)) (bst-right bst))
( else make a tree...
But now we can spot another problem: even in these cases, we need to recurse into the subtrees before we're done.
I'll digress here because too many accessor repetitions and conds makes code pretty unreadable.
Using a couple of lets (and getting rid of the unused f parameter), I ended up with this:
(define (bst-remove tree g x)
(if (empty? tree)
(bst-create-empty)
;; If the tree isn't empty, we need to recurse.
;; This work is identical for all the cases below, so
;; lift it up here.
(let ([new-left (bst-remove (bst-left tree) g x)]
[new-right (bst-remove (bst-right tree) g x)])
;; Build an appropriate tree with the new subtrees.
(if (g (bst-value tree) x)
(cond [(empty? new-left) new-right] ;; If either new subtree is empty,
[(empty? new-right) new-left] ;; use the other.
;; The complicated case. Get the new node value from the
;; right subtree and remove it from there before using it.
[else (let ([new-value (bst-max-right new-right)])
(bst-create new-value
new-left
(bst-remove new-right g new-value)))])
;; The straightforward case.
(bst-create (bst-value tree)
new-left
new-right)))))

Minimax algorithm not working as expected

I am building an AI for a game of tick-tack-toe, using the stub given in the Realm of Racket book as a basis. So far, everything has gone well. However, when I try to run my minimax function on the root of the tree, it returns a list of the lowest possible values you can get by running it (With either player as the predicate).
Here is a code dump of the function:
(define (minimax tree player depth)
(define (generate-score tree player depth)
(define won (winner (ttt-tree-board tree)))
(if (<= depth 0) 0
(+ (cond
[(equal? won player) 1]
[(false? won) 0]
[else -1])
(apply +
(for/list ([t (ttt-tree-moves tree)])
(generate-score (second t) player (sub1 depth)))))))
(for/list ([t (ttt-tree-moves tree)])
(generate-score (second t) player depth)))
This is only my minimax function, because none of the others (except for winner) need to be displayed. Here is winner:
(define (winner board)
(or
(ormap (lambda (row) (if (all-equal? row) (first row) #f)) board) ; Horizontal
(ormap (lambda (i) (if ; Vertical
(all-equal? (map (lambda (j) (list-ref (list-ref board j) i)) (stream->list (in-range (length board)))))
(list-ref (first board) i) #f))
(stream->list (in-range (length board))))
(if ; Diagonal
(all-equal? (map (lambda (i) (list-ref (list-ref board i) i)) (stream->list (in-range (length board)))))
(first (first board)) #f)
(if ; Diagonal cont.
(all-equal? (map (lambda (i) (list-ref (reverse (list-ref board i)) i)) (stream->list (in-range (length board)))))
(last (first board)) #f)))
(It is a bit sloppy, so if anyone has an idea to make it shorter, tell me your suggestion)
ttt-tree structures follow this pattern:
(ttt-tree board moves)
In which board is a 2D array corresponding to the tick-tack-toe board (with the form '((X - O) (O X -) (- X O))), and moves is a list of possible moves that can be made from that node, each of which is a list with two elements: the position that changed, and a ttt-tree which forms the next node. So (second t) refers to the next node, if t is (list-ref (ttt-tree-moves #<ttt-tree>) x)
My original thought was that there may be some error in the cond, but I'm using equal? not eq?, so I don't see how that would be a problem. The other possibility is that I'm defining winner wrong, but I have tested it many times so I don't know what could go wrong. Please help!
It turns out that the problem was that in my winner function, I never checked if the winner was "-", the empty tile. Therefore that would trigger in most cases, activating the else clause and resulting in a score of -1.

Scheme - checking structural equivalences of lists (how to use AND)

I am trying to write a program that will check the structural equivalence of some list input, whether it includes just atoms or nested sub lists.
I am having trouble with using AND, I don't even know if its possible and I cant seem to understand documentation I am looking at.
My code:
(define (structEqual a b)
(cond
(((null? car a) AND (null? car b)) (structEqual (cdr a) (cdr b)))
(((null? car a) OR (null? car b)) #f)
(((pair? car a) AND (pair? car b))
(if (= (length car a) (length car b))
(structEqual (cdr a) (cdr b))
#f))
(((pair? car a) OR (pair? car b)) #f)
(else (structEqual (cdr a) (cdr b)))))
The idea is (i think): (when I say both, i mean the current cdr of a or b)
Check if both a and b are null, then they are structurally equal
Check if only either a or b is null, then they are not structually equal
Check if both of them are pairs
If they are both pairs, then see if the length of the pair is equal, if not they are not structurally equal.
If they are not both pairs, then if one of them is a pair and the other isnt then they are not structurally equivalent.
If neither of them are pairs, then they both must be atoms, so they are structurally equivalent.
So as you can see I am trying to recursively do this by checking the equivalence of the car of a or b, and then either returning #f if they fail or moving on to the cdr of each if they are equivalent at each step.
Any help?
There is no infix operators in Scheme (or any LISP) only prefix. Every time the operator comes first. (or x (and y z q) (and y w e)) where each letter can be a complex expression. Everything that is not #f is a true value. Thus (if 4 'a 'b) evaluates to a because 4 is a true value. car needs its parentheses.
When evaluating another predicate in cond you should make use of the fact that everything up to that has been false. eg.
(define (structure-equal? a b)
(cond
((null? a) (null? b)) ; if a is null the result is if b is null
((not (pair? a)) (not (pair? b))) ; if a is not pair the result is if b is not also
((pair? b) (and (structure-equal? (car a) (car b)) ; if b is pair (both a and b is pair then) both
(structure-equal? (cdr a) (cdr b)))) ; car and cdr needs to be structurally equal
(else #f))) ; one pair the other not makes it #f
(structure-equal '(a (b (c d e) f) g . h) '(h (g (f e d) c) b . a)) ; ==> #t

Scheme - car/cdr on empty list

I wrote a big program that use car and cdr, and do:
(map car (append (map caddr lists) (map cadr lists))
When lists is list of lists in the next format ((a (b) (c)) (d (e) (f))...(x (y) (z)))
When I did it I got one list (b c e f... y z)
(Note: b,c,...y,z is a list of numbers; a,d...x is a symbol)
But now, I found that b,c,...,y,z can be also empty list, and It gives the next error:
car: expects argument of type <pair>; given ()
What can I do?
Have you tried filtering away empty lists before your map? Something like this:
(map car (filter pair? (append (map caddr lists) (map cadr lists))))
The fundamental issue is that () is not a pair while car only acts on pairs. The simplest solution is just to get rid of everything that isn't a pair before mapping car onto the list; this is what (filter pair? ...) does.
Here's my shot. It's a straight solution, without using map, filter; in that way, I avoid going over and over the elements of the list constructing intermediate lists - except that I used a reverse operation for preserving the original order of the elements, if that's not an issue, remove reverse. For each element in the original list, if either the (b)... or (c)... part is empty, it's skipped.
(define (process lists)
(let loop ((l (reverse lists))
(a '())
(b '()))
(cond ((null? l)
(append b a))
((or (not (pair? (cadar l))) (not (pair? (caddar l))))
(loop (cdr l) a b))
(else
(loop (cdr l) (cons (caadar l) a) (cons (car (caddar l)) b))))))

Im trying to access a N dimensional vector but I get into infinite recursion

I want to access a n-dimensional vector but somehow (empty? '()) keeps returning false.
;; access n dimensional vectors
;; (access-nd vector a-list-of-numbers) -> element
;; (access-nd (vector (vector 'x 'y) 'a 'b)) 0 1 ) -> x
(define (access-nd avector . alist)
(cond
((and (not(empty? alist)) (vector? avector))
(vector-ref (access-nd avector (rest alist)) (first alist)))
(else avector)))
Please Help.
Edit: CORRECTED CODE
(define (access-nd avector . alist)
(cond
((and (not(empty? alist)) (vector? avector))
(apply access-nd (vector-ref avector (first alist)) (rest alist)))
(else avector)))
Most likely, that one line should read:
(vector-ref (apply access-nd avector (rest alist)) (first alist)))
Without the "apply", alist will never be empty. Here's why:
In the definition of access-nd the alist parameter is an optional parameters list; it's separated with a dot from normal positional parameters. This means access-nd can be called with 1-n parameters. Any parameters after the first one are collected to a list and bound to alist. For example, a call like
(access-nd v 1 2 3)
will cause alist to be bound to the list (1 2 3). Similarly, this call in your original code:
(access-nd avector (rest alist))
will cause alist to be bound to a list with one element. That's why alist will never be empty.
Scheme's apply, on the other hand, takes a list of arguments as the last parameter and calls the function as if they were passed to it in the normal way.

Resources