Cheap trick to make logic gates enforce electron directionality.

This commit is contained in:
David Thompson 2024-05-23 17:28:09 -04:00
parent 1b51dde9bc
commit 00df10dc12

View file

@ -28,6 +28,22 @@
('electron-head 'electron-tail) ('electron-head 'electron-tail)
('electron-tail 'copper))) ('electron-tail 'copper)))
(define (electron-head-count neighbor-grid)
(define (check state)
(match state
('electron-head 1)
(_ 0)))
(match neighbor-grid
(#(a b c d '_ e f g h)
(+ (check a)
(check b)
(check c)
(check d)
(check e)
(check f)
(check g)
(check h)))))
(define* (^cell bcom #:optional val) (define* (^cell bcom #:optional val)
(case-lambda (case-lambda
(() val) (() val)
@ -44,8 +60,8 @@
(('post-tick grid-info) #f) (('post-tick grid-info) #f)
(('enter obj grid-info) #f) (('enter obj grid-info) #f)
(('exit obj grid-info) #f) (('exit obj grid-info) #f)
(('wire-state grid-info) #f) (('wire-state grid-info from from-x from-y) #f)
(('update-wire-state grid-info) #f) (('update-wire-state grid-info neighbor-grid) #f)
(('alive?) #t) (('alive?) #t)
(('describe) `(exit ,position)) (('describe) `(exit ,position))
(('collide other offset grid-info) #f))) (('collide other offset grid-info) #f)))
@ -60,15 +76,15 @@
(('post-tick grid-info) #f) (('post-tick grid-info) #f)
(('enter obj grid-info) #f) (('enter obj grid-info) #f)
(('exit obj grid-info) #f) (('exit obj grid-info) #f)
(('wire-state grid-info) (('wire-state grid-info from from-x from-y)
(match type (match type
((or 'copper 'electron-head 'electron-tail) ((or 'copper 'electron-head 'electron-tail)
type) type)
(_ #f))) (_ #f)))
(('update-wire-state grid-info) (('update-wire-state grid-info neighbor-grid)
(match type (match type
((or 'copper 'electron-head 'electron-tail) ((or 'copper 'electron-head 'electron-tail)
(let* ((neighbors ($ grid-info 'wireworld-neighbor-count x y)) (let* ((neighbors (electron-head-count neighbor-grid))
(type (wireworld-next type neighbors))) (type (wireworld-next type neighbors)))
(bcom (^wall bcom x y type)))) (bcom (^wall bcom x y type))))
(_ #f))) (_ #f)))
@ -87,17 +103,17 @@
(('post-tick grid-info) #f) (('post-tick grid-info) #f)
(('enter obj grid-info) #f) (('enter obj grid-info) #f)
(('exit obj grid-info) #f) (('exit obj grid-info) #f)
(('wire-state grid-info) (('wire-state grid-info from from-x from-y)
(match type (match type
((or 'copper 'electron-head 'electron-tail) ((or 'copper 'electron-head 'electron-tail)
type) type)
(_ #f))) (_ #f)))
(('update-wire-state grid-info) (('update-wire-state grid-info neighbor-grid)
(match type (match type
((or 'copper 'electron-head 'electron-tail) ((or 'copper 'electron-head 'electron-tail)
(match ($ position) (match ($ position)
(#(x y z) (#(x y z)
(let* ((neighbors ($ grid-info 'wireworld-neighbor-count x y)) (let* ((neighbors (electron-head-count neighbor-grid))
(type (wireworld-next type neighbors))) (type (wireworld-next type neighbors)))
(bcom (^block bcom x y type)))))) (bcom (^block bcom x y type))))))
(_ #f))) (_ #f)))
@ -135,12 +151,12 @@
(('post-tick grid-info) #f) (('post-tick grid-info) #f)
(('enter obj grid-info) #f) (('enter obj grid-info) #f)
(('exit obj grid-info) #f) (('exit obj grid-info) #f)
(('wire-state grid-info) (('wire-state grid-info from from-x from-y)
(match ($ timer) (match ($ timer)
(0 'electron-head) (0 'electron-head)
(1 'electron-tail) (1 'electron-tail)
(_ 'copper))) (_ 'copper)))
(('update-wire-state grid-info) #f) (('update-wire-state grid-info neighbor-grid) #f)
(('alive?) #t) (('alive?) #t)
(('describe) `(clock-emitter ,position)) (('describe) `(clock-emitter ,position))
(('collide other offset grid-info) #f))) (('collide other offset grid-info) #f)))
@ -168,13 +184,13 @@
(('deactivate grid-info) (('deactivate grid-info)
($ on? #f) ($ on? #f)
($ grid-info 'append-event `(emitter-off ,x ,y))) ($ grid-info 'append-event `(emitter-off ,x ,y)))
(('wire-state grid-info) (('wire-state grid-info from from-x from-y)
(and ($ on?) (and ($ on?)
(match ($ timer) (match ($ timer)
(0 'electron-head) (0 'electron-head)
(1 'electron-tail) (1 'electron-tail)
(_ 'copper)))) (_ 'copper))))
(('update-wire-state grid-info) #f) (('update-wire-state grid-info neighbor-grid) #f)
(('alive?) #t) (('alive?) #t)
(('on?) ($ on?)) (('on?) ($ on?))
(('describe) `(switched-emitter ,position ,($ on?))) (('describe) `(switched-emitter ,position ,($ on?)))
@ -207,8 +223,8 @@
(target (target
($ grid-info 'append-event `(floor-switch-off ,x ,y)) ($ grid-info 'append-event `(floor-switch-off ,x ,y))
($ target 'deactivate grid-info))))) ($ target 'deactivate grid-info)))))
(('wire-state grid-info) #f) (('wire-state grid-info from from-x from-y) #f)
(('update-wire-state grid-info) #f) (('update-wire-state grid-info neighbor-grid) #f)
(('alive?) #t) (('alive?) #t)
(('describe) `(floor-switch ,position ,($ on?))) (('describe) `(floor-switch ,position ,($ on?)))
(('collide other offset grid-info) (('collide other offset grid-info)
@ -230,8 +246,8 @@
(('post-tick grid-info) #f) (('post-tick grid-info) #f)
(('enter obj grid-info) #f) (('enter obj grid-info) #f)
(('exit obj grid-info) #f) (('exit obj grid-info) #f)
(('wire-state grid-info) #f) (('wire-state grid-info from from-x from-y) #f)
(('update-wire-state grid-info) (('update-wire-state grid-info neighbor-grid)
(if ($ on?) (if ($ on?)
(let ((t (1- ($ timer)))) (let ((t (1- ($ timer))))
($ timer t) ($ timer t)
@ -242,7 +258,7 @@
(target (target
($ grid-info 'append-event `(electric-switch-off ,x ,y)) ($ grid-info 'append-event `(electric-switch-off ,x ,y))
($ target 'deactivate grid-info))))) ($ target 'deactivate grid-info)))))
(when (>= ($ grid-info 'wireworld-neighbor-count x y) 1) (when (>= (electron-head-count neighbor-grid) 1)
($ on? #t) ($ on? #t)
($ timer 2) ($ timer 2)
(match (first-non-player-occupant grid-info target-x target-y) (match (first-non-player-occupant grid-info target-x target-y)
@ -273,8 +289,8 @@
(('post-tick grid-info) #f) (('post-tick grid-info) #f)
(('enter obj grid-info) #f) (('enter obj grid-info) #f)
(('exit obj grid-info) #f) (('exit obj grid-info) #f)
(('wire-state grid-info) ($ state)) (('wire-state grid-info from from-x from-y) ($ state))
(('update-wire-state grid-info) (('update-wire-state grid-info neighbor-grid)
(match ($ state) (match ($ state)
('electron-head ($ state 'electron-tail)) ('electron-head ($ state 'electron-tail))
('electron-tail ($ state 'copper)) ('electron-tail ($ state 'copper))
@ -284,7 +300,7 @@
($ state 'electron-head) ($ state 'electron-head)
($ electron? #f) ($ electron? #f)
($ grid-info 'append-event `(receive-electron ,x ,y))) ($ grid-info 'append-event `(receive-electron ,x ,y)))
(let ((neighbors ($ grid-info 'wireworld-neighbor-count x y))) (let ((neighbors (electron-head-count neighbor-grid)))
(if (<= 1 neighbors 2) (if (<= 1 neighbors 2)
(begin (begin
($ state 'electron-head) ($ state 'electron-head)
@ -307,8 +323,8 @@
(('post-tick grid-info) #f) (('post-tick grid-info) #f)
(('enter obj grid-info) #f) (('enter obj grid-info) #f)
(('exit obj grid-info) #f) (('exit obj grid-info) #f)
(('wire-state grid-info) #f) (('wire-state grid-info from from-x from-y) #f)
(('update-wire-state grid-info) #f) (('update-wire-state grid-info neighbor-grid) #f)
(('alive?) (not ($ picked-up?))) (('alive?) (not ($ picked-up?)))
(('describe) `(gem ,position)) (('describe) `(gem ,position))
(('collide other offset grid-info) (('collide other offset grid-info)
@ -325,8 +341,8 @@
(('post-tick grid-info) #f) (('post-tick grid-info) #f)
(('enter obj grid-info) #f) (('enter obj grid-info) #f)
(('exit obj grid-info) #f) (('exit obj grid-info) #f)
(('wire-state grid-info) #f) (('wire-state grid-info from from-x from-y) #f)
(('update-wire-state grid-info) #f) (('update-wire-state grid-info neighbor-grid) #f)
(('alive?) #t) (('alive?) #t)
(('describe) `(ghost-gem ,position)) (('describe) `(ghost-gem ,position))
(('collide other offset grid-info) #f))) (('collide other offset grid-info) #f)))
@ -347,8 +363,8 @@
(('deactivate grid-info) (('deactivate grid-info)
($ open? #f) ($ open? #f)
($ grid-info 'append-event `(gate-close ,x ,y))) ($ grid-info 'append-event `(gate-close ,x ,y)))
(('wire-state grid-info) #f) (('wire-state grid-info from from-x from-y) #f)
(('update-wire-state grid-info) #f) (('update-wire-state grid-info neighbor-grid) #f)
(('alive?) #t) (('alive?) #t)
(('open?) ($ open?)) (('open?) ($ open?))
(('describe) `(gate ,position ,($ open?))) (('describe) `(gate ,position ,($ open?)))
@ -364,13 +380,26 @@
(('post-tick grid-info) #f) (('post-tick grid-info) #f)
(('enter obj grid-info) #f) (('enter obj grid-info) #f)
(('exit obj grid-info) #f) (('exit obj grid-info) #f)
(('wire-state grid-info) ($ state)) (('wire-state grid-info from from-x from-y)
(('update-wire-state grid-info) ;; We are compressing what would take many cells in the actual
;; Wireworld into a single tile. A naive approach to this would
;; send electrons flowing backwards through the logic gates. So,
;; need to play a trick to make enforce directionality. The
;; trick is that we know who is asking for our wire state. If
;; the object is opposes the direction we are pushing electrons,
;; then we tell them we're just copper. Otherwise, we reveal our
;; true state. Sneaky, sneaky.
(match direction
('left (if (> from-x x) 'copper ($ state)))
('right (if (< from-x x) 'copper ($ state)))
('up (if (> from-y y) 'copper ($ state)))
('down (if (< from-y y) 'copper ($ state)))))
(('update-wire-state grid-info neighbor-grid)
(match ($ state) (match ($ state)
('electron-head ($ state 'electron-tail)) ('electron-head ($ state 'electron-tail))
('electron-tail ($ state 'copper)) ('electron-tail ($ state 'copper))
('copper ('copper
(update-wire-state state ($ grid-info 'wireworld-neighbor-grid x y))))) (update-wire-state state neighbor-grid))))
(('alive?) #t) (('alive?) #t)
(('describe) `(,name ,position ,direction ,($ state))) (('describe) `(,name ,position ,direction ,($ state)))
(('collide other offset grid-info) #f))) (('collide other offset grid-info) #f)))
@ -626,7 +655,7 @@
(_ (lp rest))))))))) (_ (lp rest)))))))))
(('enter obj grid-info) #f) (('enter obj grid-info) #f)
(('exit obj grid-info) #f) (('exit obj grid-info) #f)
(('wire-state grid-info) #f) (('wire-state grid-info from from-x from-y) #f)
(('alive?) ($ alive?)) (('alive?) ($ alive?))
(('describe) `(player ,($ position), ($ alive?))) (('describe) `(player ,($ position), ($ alive?)))
(('collide other offset grid-info) (('collide other offset grid-info)
@ -692,62 +721,35 @@
(vector-set! grid (+ (* y width) x) val)) (vector-set! grid (+ (* y width) x) val))
(define grid (make-grid '())) (define grid (make-grid '()))
(define wire-grid (make-grid #f)) (define (wire-state-at who who-x who-y target-x target-y)
(define (refresh-wire-grid) (match ($ (grid-ref/wrap grid target-x target-y))
(for-each-coord (() #f)
(lambda (x y) ((obj . _)
(let ((obj-cell (grid-ref grid x y)) ($ obj 'wire-state grid-info who who-x who-y))))
(wire-cell (grid-ref wire-grid x y)))
(match ($ obj-cell)
(() #f)
;; TODO: Handle tiles with many occupants. Might not be
;; necessary in practice. Actually this *WILL* cause
;; problems for electron warps, at least, since they are
;; invisible and the player can stand over them.
((refr . _)
($ wire-cell ($ refr 'wire-state grid-info))))))))
(define (wire-state-at x y)
($ (grid-ref/wrap wire-grid x y)))
(define (neighbor-count x y)
(define (check x y)
(match (wire-state-at x y)
('electron-head 1)
(_ 0)))
(+ (check (- x 1) (- y 1))
(check x (- y 1))
(check (+ x 1) (- y 1))
(check (- x 1) y)
(check (+ x 1) y)
(check (- x 1) (+ y 1))
(check x (+ y 1))
(check (+ x 1) (+ y 1))))
;; flattened 3x3 grid of neighbor states. '_' used to mark the ;; flattened 3x3 grid of neighbor states. '_' used to mark the
;; center. ;; center.
(define (neighbor-grid x y) (define (neighbor-grid obj)
(vector (wire-state-at (- x 1) (- y 1)) (match ($ obj 'position)
(wire-state-at x (- y 1)) (#(x y z)
(wire-state-at (+ x 1) (- y 1)) (vector (wire-state-at obj x y (- x 1) (- y 1))
(wire-state-at obj x y x (- y 1))
(wire-state-at obj x y (+ x 1) (- y 1))
(wire-state-at (- x 1) y) (wire-state-at obj x y (- x 1) y)
'_ '_
(wire-state-at (+ x 1) y) (wire-state-at obj x y (+ x 1) y)
(wire-state-at (- x 1) (+ y 1)) (wire-state-at obj x y (- x 1) (+ y 1))
(wire-state-at x (+ y 1)) (wire-state-at obj x y x (+ y 1))
(wire-state-at (+ x 1) (+ y 1)))) (wire-state-at obj x y (+ x 1) (+ y 1))))))
;; Read-only access to query the grid. ;; Read-only access to query the grid, but can write events.
(define (^grid-info bcom) (define (^grid-info bcom)
(match-lambda* (match-lambda*
(('occupied? x y) (('occupied? x y)
(not (null? ($ (grid-ref grid x y))))) (not (null? ($ (grid-ref grid x y)))))
(('occupants x y) (('occupants x y)
($ (grid-ref grid x y))) ($ (grid-ref grid x y)))
;; How many electron heads around (x, y)?
(('wireworld-neighbor-count x y)
(neighbor-count x y))
(('wireworld-neighbor-grid x y)
(neighbor-grid x y))
(('append-event event) (('append-event event)
($ event-log 'append event)))) ($ event-log 'append event))))
(define grid-info (spawn ^grid-info)) (define grid-info (spawn ^grid-info))
@ -827,10 +829,10 @@
;; Tick all the non-player objects. ;; Tick all the non-player objects.
(iter-objects tick-object) (iter-objects tick-object)
;; Advance Wirewold simulation. ;; Advance Wirewold simulation.
(refresh-wire-grid) (let ((neighbor-grids (map neighbor-grid ($ objects))))
(for-each (lambda (obj) (for-each (lambda (obj neighbor-grid)
($ obj 'update-wire-state grid-info)) ($ obj 'update-wire-state grid-info neighbor-grid))
($ objects)) ($ objects) neighbor-grids))
;; Run post-tick hooks. ;; Run post-tick hooks.
($ player 'post-tick grid-info) ($ player 'post-tick grid-info)
(iter-objects (lambda (obj) ($ obj 'post-tick grid-info))))) (iter-objects (lambda (obj) ($ obj 'post-tick grid-info)))))