我正在为一个班级开发一个球拍程序,我完全不知道如何实现其中一个功能。
该程序使用 Big-Bang 并且应该实现一个简单的 Space Invaders 游戏。
除了一件,我什么都可以工作,那就是 - 如何处理导弹与入侵者相撞的情况。我挣扎的原因是我不知道如何编写一个函数,其中我有两个任意大小的列表,我必须检查一个列表中每个对象的字段与另一个列表中的每个对象并删除一个对象如果它们具有相同的值,则在每个列表中。
世界状态是游戏:
(define-struct game (invaders missiles tank))
入侵者和导弹都是清单。
为了产生游戏的下一个状态,我实现了一个名为“tock”的函数。
通常,我会这样做:
(define (tock s)
(make-game (next-invaders (game-invaders s))
(next-missiles (game-missiles s))
(next-tank (game-tank s)))
但是由于入侵者和导弹列表的内容可能会由于碰撞而相互影响,我不能简单地单独更新位置并继续前进,我必须删除任何碰撞然后更新位置。
所以我试过:
(define (tock s)
(make-game (check-collision (game-invaders s)
(game-missiles s)
(game-tank s))
但这使得 check-collision 需要一个坦克,它不需要。
(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))))
在这个版本中,我有一个调用next-invaders
入侵者和导弹列表的函数,以及一个next-missiles
获取导弹和入侵者列表的函数。第一个函数针对每个导弹检查每个入侵者,尝试移除任何碰撞的入侵者并返回剩余的入侵者。第二个函数检查每个导弹与每个入侵者的关系,并尝试移除任何相撞的导弹并返回剩余的导弹。答案应该是一样的,但这是重复的工作,我担心可能出现的竞争状况。我不知道如何构造一个表达式,其中一个函数只需要两个字段,另一个需要三个字段,但我仍然会产生游戏的下一个状态。
这是一个例子next-invaders
。如果没有入侵者,它什么也不做。如果有入侵者但没有导弹,它只是移动每个入侵者(move-invader)
并递归调用自身迭代所有入侵者。如果既有导弹又有入侵者,则检查列表中的第一个入侵者与列表中的每一个导弹之间是否发生碰撞;所以检查碰撞是递归的。
(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))]))
check-collision
是否是从入侵者列表中“删除”碰撞入侵者的正确方法的“答案” ?
(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))]))
这是对每个列表的每个元素进行相互测试的正确方法吗?
更新:仍然在这个问题上兜圈子。check-collision 有效,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)))