scheme - 在两个列表上运行的函数

标签 scheme lisp racket racket-student-languages

我正在为一个类(class)开发一个 Racket 程序,我完全不知所措 如何实现其中一项功能。

该程序使用 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))

但是这使得检查碰撞需要一个坦克,这是它不需要的。

(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,它获取导弹和入侵者的列表。第一个函数针对每个导弹检查每个入侵者,尝试移除任何碰撞的入侵者并返回剩余的入侵者。第二个函数检查每个导弹是否与每个入侵者相对应,并尝试移除任何相撞的导弹并返回剩余的导弹。答案应该是相同的,但这是重复的工作,我担心可能存在竞争条件。我不知道如何构建一个函数只需要两个字段而另一个函数需要三个字段的表达式 而且我仍然会制作游戏的下一个状态。

这是下一个入侵者的例子。如果没有入侵者,它什么都不做。如果有入侵者但没有导弹, 它只是移动每个入侵者 (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 都有效,但是当我返回 missile-function 时,我不知道如何指示在 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)))

最佳答案

我没有查看所有代码,但一般的解决方案是使用一个函数获取导弹和入侵者的列表,检查所有碰撞,然后通过返回一对列表来返回两个更新的列表。所以像这样:

(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))

关于scheme - 在两个列表上运行的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52864704/

相关文章:

events - 如何处理 Racket 中的 GUI 退出?

scheme - 清除小厨房方案上的屏幕

macros - 使用 Lisp 宏创建类似的函数

c - 将变量参数 LISP 函数映射到 C 函数 - C

parameters - 普通口齿不清 : Working with &rest parameters

namespaces - Racket :评估,命名空间附加模块与命名空间要求

racket - 4294967087 作为 Racket(随机)函数的参数限制有什么意义?

lambda - 在 SICP 中使用 lambda 定义 cons/car/cdr

string - 从方案中的字符串中删除重复的字符

lambda - 在方案中将 let 转换为 lambda