-
Notifications
You must be signed in to change notification settings - Fork 0
/
map-builder.lisp
149 lines (138 loc) · 4.74 KB
/
map-builder.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
(in-package :lambda-lifter)
(defparameter *map-defaults*
'((:WATER . 0)
(:FLOODING . 0)
(:WATERPROOF . 10)
(:GROWTH . 25)
(:RAZORS . 0)))
(defmacro with-meta-bind ((metadata &rest vars) &body body)
`(let (,@(iter (for var in vars)
(collect `(,var (second (assoc ,(form-keyword var) ,metadata))))))
,@body))
(defun meta-value (metadata key)
(find-if (lambda (meta) (eq (car meta) key)) metadata))
(defun map-has-flooding-p (world objects metadata)
(declare (ignore world objects metadata))
(with-meta-bind (metadata water flooding)
(or (plusp water)
(plusp flooding))))
(defun map-has-portals-p (world objects metadata)
(declare (ignore world metadata))
(or (funcall objects :portal-a)
(funcall objects :portal-b)
(funcall objects :portal-c)
(funcall objects :portal-d)
(funcall objects :portal-e)
(funcall objects :portal-f)
(funcall objects :portal-g)
(funcall objects :portal-h)
(funcall objects :portal-i)))
(defun map-has-beards-p (world objects metadata)
(declare (ignore world metadata))
(funcall objects :beard))
(defun map-has-horocks-p (world objects metadata)
(declare (ignore world metadata))
(funcall objects :horock))
(defun make-mine (stream)
(let ((objects (make-hash-table :test 'eq)))
(let* ((metadata (apply-map-parser stream
(lambda (type width x y)
(push (+ (* (1- y) width) (1- x))
(gethash type objects))))))
(with-meta-bind (metadata width height growth razors)
(assert (and width height) nil "Either width or height in metadata not found")
(let ((world (make-array (* width height) :initial-element nil)))
(iter (for (type objects-list) in-hashtable objects)
(iter (for coord in objects-list)
(setf (elt world coord) type)
(for parsed-coord = (complex (1+ (mod coord width))
(- height (truncate coord width))))
(collect parsed-coord into parsed-coords)
(finally (setf (gethash type objects) parsed-coords))))
(setf (gethash :razors objects) razors)
(setf (gethash :growth objects) (when growth (- growth 1)))
(values
(lambda (x y)
(assert (and (<= 1 x width) (<= 1 y height)))
(elt world (+ (* (- height y) width) (1- x))))
(lambda (type)
(case type
(t (gethash type objects))))
(lambda () nil)
metadata))))))
(defun apply-map-parser (stream cell-receiver)
(multiple-value-bind (map width height)
(iter
(with state = :reading-map)
(with map-width = 0)
(with map-height = 0)
(for line in-stream stream using #'read-line)
(when (zerop (length line))
(setf state nil))
(when (eq state :reading-map)
(setf map-width (max map-width (length line)))
(incf map-height))
(collect line into lines)
(finally (return (values lines map-width map-height))))
(iter outer
(with state = :reading-map)
(for line in map)
(for rev-row-index from 1)
(when (zerop (length line))
(setf state :reading-metadata)
(next-iteration))
(ecase state
(:reading-map
(iter (for char in-string line)
(for cell-index from 1)
(assert (<= cell-index width))
(unless (char= char #\Space)
(funcall cell-receiver
(ecase char
(#\R :robot)
(#\# :wall)
(#\* :rock)
(#\\ :lambda)
(#\L :closed-lambda-lift)
(#\O :open-lambda-lift)
(#\. :earth)
(#\A :portal-a)
(#\B :portal-b)
(#\C :portal-c)
(#\D :portal-d)
(#\E :portal-e)
(#\F :portal-f)
(#\G :portal-g)
(#\H :portal-h)
(#\I :portal-i)
(#\1 :target-1)
(#\2 :target-2)
(#\3 :target-3)
(#\4 :target-4)
(#\5 :target-5)
(#\6 :target-6)
(#\7 :target-7)
(#\8 :target-8)
(#\9 :target-9)
(#\W :beard)
(#\! :razor)
(#\@ :horock))
width
cell-index
rev-row-index))))
(:reading-metadata
(for metasplit = (split-sequence:split-sequence #\Space line))
(let ((pname (form-keyword (string-upcase (first metasplit)))))
(collect (case pname
(:TRAMPOLINE (list (form-keyword (format nil "PORTAL-~a" (string-upcase (second metasplit))))
(form-keyword (format nil "TARGET-~a" (fourth metasplit)))))
(t (list pname (parse-integer (second metasplit)))))
into metadata))))
(finally
(return-from outer (append (list (list :best 0 nil)
(list :width width)
(list :height height))
metadata
(iter (for (param . default-value) in *map-defaults*)
(unless (assoc param metadata)
(collect (list param default-value))))))))))