HideIfDef mode bug fixes and enhancements. This is #2 of 3 patches based
[bpt/emacs.git] / lisp / play / landmark.el
CommitLineData
5abdc915 1;;; landmark.el --- neural-network robot that learns landmarks
a7b88742 2
ba318903 3;; Copyright (C) 1996-1997, 2000-2014 Free Software Foundation, Inc.
178fc2d3 4
9781053a 5;; Author: Terrence Brannon (was: <brannon@rana.usc.edu>)
178fc2d3 6;; Created: December 16, 1996 - first release to usenet
2a9a5ea8 7;; Keywords: games, neural network, adaptive search, chemotaxis
a7b88742 8
178fc2d3
RS
9;; This file is part of GNU Emacs.
10
b1fc2b50 11;; GNU Emacs is free software: you can redistribute it and/or modify
178fc2d3 12;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
178fc2d3
RS
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
b1fc2b50 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
178fc2d3 23
178fc2d3 24
6e44da43 25;;; Commentary:
2a9a5ea8
GM
26
27;; To try this, just type: M-x landmark-test-run
28
4d0143e6 29;; Landmark is a relatively non-participatory game in which a robot
80cb310d
SM
30;; attempts to maneuver towards a tree at the center of the window
31;; based on unique olfactory cues from each of the 4 directions. If
32;; the smell of the tree increases, then the weights in the robot's
33;; brain are adjusted to encourage this odor-driven behavior in the
34;; future. If the smell of the tree decreases, the robots weights are
35;; adjusted to discourage a correct move.
36
37;; In laymen's terms, the search space is initially flat. The point
38;; of training is to "turn up the edges of the search space" so that
39;; the robot rolls toward the center.
40
41;; Further, do not become alarmed if the robot appears to oscillate
42;; back and forth between two or a few positions. This simply means
43;; it is currently caught in a local minimum and is doing its best to
44;; work its way out.
45
46;; The version of this program as described has a small problem. a
47;; move in a net direction can produce gross credit assignment. for
48;; example, if moving south will produce positive payoff, then, if in
49;; a single move, one moves east,west and south, then both east and
50;; west will be improved when they shouldn't
51
4d0143e6 52;; Many thanks to Yuri Pryadkin <yuri@rana.usc.edu> for this
80cb310d 53;; concise problem description.
178fc2d3 54
178fc2d3 55;;;_* Require
a464a6c7 56(eval-when-compile (require 'cl-lib))
178fc2d3 57
178fc2d3
RS
58;;;_* From Gomoku
59
6e44da43
PJ
60;;; Code:
61
4d0143e6 62(defgroup landmark nil
323f7c49 63 "Neural-network robot that learns landmarks."
4d0143e6 64 :prefix "landmark-"
323f7c49
SE
65 :group 'games)
66
178fc2d3
RS
67;;;_ + THE BOARD.
68
178fc2d3
RS
69;; The board is a rectangular grid. We code empty squares with 0, X's with 1
70;; and O's with 6. The rectangle is recorded in a one dimensional vector
71;; containing padding squares (coded with -1). These squares allow us to
4fffd73b 72;; detect when we are trying to move out of the board. We denote a square by
178fc2d3 73;; its (X,Y) coords, or by the INDEX corresponding to them in the vector. The
4d0143e6 74;; leftmost topmost square has coords (1,1) and index landmark-board-width + 2.
178fc2d3
RS
75;; Similarly, vectors between squares may be given by two DX, DY coords or by
76;; one DEPL (the difference between indexes).
77
4d0143e6
JA
78(defvar landmark-board-width nil
79 "Number of columns on the Landmark board.")
80(defvar landmark-board-height nil
81 "Number of lines on the Landmark board.")
178fc2d3 82
4d0143e6
JA
83(defvar landmark-board nil
84 "Vector recording the actual state of the Landmark board.")
178fc2d3 85
4d0143e6
JA
86(defvar landmark-vector-length nil
87 "Length of landmark-board vector.")
178fc2d3 88
4d0143e6 89(defvar landmark-draw-limit nil
178fc2d3
RS
90 ;; This is usually set to 70% of the number of squares.
91 "After how many moves will Emacs offer a draw?")
92
4d0143e6 93(defvar landmark-cx 0
a7b88742
KH
94 "This is the x coordinate of the center of the board.")
95
4d0143e6 96(defvar landmark-cy 0
a7b88742
KH
97 "This is the y coordinate of the center of the board.")
98
4d0143e6 99(defvar landmark-m 0
a7b88742
KH
100 "This is the x dimension of the playing board.")
101
4d0143e6 102(defvar landmark-n 0
a7b88742
KH
103 "This is the y dimension of the playing board.")
104
105
4d0143e6 106(defun landmark-xy-to-index (x y)
178fc2d3 107 "Translate X, Y cartesian coords into the corresponding board index."
4d0143e6 108 (+ (* y landmark-board-width) x y))
178fc2d3 109
4d0143e6 110(defun landmark-index-to-x (index)
178fc2d3 111 "Return corresponding x-coord of board INDEX."
4d0143e6 112 (% index (1+ landmark-board-width)))
178fc2d3 113
4d0143e6 114(defun landmark-index-to-y (index)
178fc2d3 115 "Return corresponding y-coord of board INDEX."
4d0143e6 116 (/ index (1+ landmark-board-width)))
178fc2d3 117
4d0143e6
JA
118(defun landmark-init-board ()
119 "Create the landmark-board vector and fill it with initial values."
120 (setq landmark-board (make-vector landmark-vector-length 0))
178fc2d3 121 ;; Every square is 0 (i.e. empty) except padding squares:
4d0143e6
JA
122 (let ((i 0) (ii (1- landmark-vector-length)))
123 (while (<= i landmark-board-width) ; The squares in [0..width] and in
124 (aset landmark-board i -1) ; [length - width - 1..length - 1]
125 (aset landmark-board ii -1) ; are padding squares.
178fc2d3
RS
126 (setq i (1+ i)
127 ii (1- ii))))
128 (let ((i 0))
4d0143e6
JA
129 (while (< i landmark-vector-length)
130 (aset landmark-board i -1) ; and also all k*(width+1)
131 (setq i (+ i landmark-board-width 1)))))
178fc2d3 132
a7b88742 133;;;_ + DISPLAYING THE BOARD.
178fc2d3 134
a7b88742
KH
135;; You may change these values if you have a small screen or if the squares
136;; look rectangular, but spacings SHOULD be at least 2 (MUST BE at least 1).
137
4d0143e6 138(defconst landmark-square-width 2
fb7ada5f 139 "Horizontal spacing between squares on the Landmark board.")
a7b88742 140
4d0143e6 141(defconst landmark-square-height 1
fb7ada5f 142 "Vertical spacing between squares on the Landmark board.")
a7b88742 143
4d0143e6 144(defconst landmark-x-offset 3
fb7ada5f 145 "Number of columns between the Landmark board and the side of the window.")
178fc2d3 146
4d0143e6 147(defconst landmark-y-offset 1
fb7ada5f 148 "Number of lines between the Landmark board and the top of the window.")
178fc2d3
RS
149
150
4d0143e6 151;;;_ + LANDMARK MODE AND KEYMAP.
178fc2d3 152
4d0143e6
JA
153(defcustom landmark-mode-hook nil
154 "If non-nil, its value is called on entry to Landmark mode."
323f7c49 155 :type 'hook
4d0143e6 156 :group 'landmark)
178fc2d3 157
4d0143e6 158(defvar landmark-mode-map
a0310a6c
DN
159 (let ((map (make-sparse-keymap)))
160 ;; Key bindings for cursor motion.
4d0143e6
JA
161 (define-key map "y" 'landmark-move-nw) ; y
162 (define-key map "u" 'landmark-move-ne) ; u
163 (define-key map "b" 'landmark-move-sw) ; b
164 (define-key map "n" 'landmark-move-se) ; n
a0310a6c
DN
165 (define-key map "h" 'backward-char) ; h
166 (define-key map "l" 'forward-char) ; l
4d0143e6
JA
167 (define-key map "j" 'landmark-move-down) ; j
168 (define-key map "k" 'landmark-move-up) ; k
a0310a6c 169
4d0143e6
JA
170 (define-key map [kp-7] 'landmark-move-nw)
171 (define-key map [kp-9] 'landmark-move-ne)
172 (define-key map [kp-1] 'landmark-move-sw)
173 (define-key map [kp-3] 'landmark-move-se)
a0310a6c
DN
174 (define-key map [kp-4] 'backward-char)
175 (define-key map [kp-6] 'forward-char)
4d0143e6
JA
176 (define-key map [kp-2] 'landmark-move-down)
177 (define-key map [kp-8] 'landmark-move-up)
a0310a6c 178
4d0143e6
JA
179 (define-key map "\C-n" 'landmark-move-down) ; C-n
180 (define-key map "\C-p" 'landmark-move-up) ; C-p
a0310a6c
DN
181
182 ;; Key bindings for entering Human moves.
4d0143e6
JA
183 (define-key map "X" 'landmark-human-plays) ; X
184 (define-key map "x" 'landmark-human-plays) ; x
185
186 (define-key map " " 'landmark-start-robot) ; SPC
187 (define-key map [down-mouse-1] 'landmark-start-robot)
188 (define-key map [drag-mouse-1] 'landmark-click)
189 (define-key map [mouse-1] 'landmark-click)
190 (define-key map [down-mouse-2] 'landmark-click)
191 (define-key map [mouse-2] 'landmark-mouse-play)
192 (define-key map [drag-mouse-2] 'landmark-mouse-play)
193
194 (define-key map [remap previous-line] 'landmark-move-up)
195 (define-key map [remap next-line] 'landmark-move-down)
196 (define-key map [remap beginning-of-line] 'landmark-beginning-of-line)
197 (define-key map [remap end-of-line] 'landmark-end-of-line)
198 (define-key map [remap undo] 'landmark-human-takes-back)
199 (define-key map [remap advertised-undo] 'landmark-human-takes-back)
a0310a6c 200 map)
4d0143e6 201 "Local keymap to use in Landmark mode.")
178fc2d3 202
a0310a6c 203
178fc2d3 204
4d0143e6 205(defvar landmark-emacs-won ()
fb7ada5f 206 "For making font-lock use the winner's face for the line.")
178fc2d3 207
4d0143e6 208(defface landmark-font-lock-face-O '((((class color)) :foreground "red")
2af34f25 209 (t :weight bold))
44e97401 210 "Face to use for Emacs's O."
2af34f25 211 :version "22.1"
4d0143e6 212 :group 'landmark)
178fc2d3 213
4d0143e6 214(defface landmark-font-lock-face-X '((((class color)) :foreground "green")
2af34f25 215 (t :weight bold))
67d110f1 216 "Face to use for your X."
2af34f25 217 :version "22.1"
4d0143e6
JA
218 :group 'landmark)
219
220(defvar landmark-font-lock-keywords
221 '(("O" . 'landmark-font-lock-face-O)
222 ("X" . 'landmark-font-lock-face-X)
223 ("[-|/\\]" 0 (if landmark-emacs-won
224 'landmark-font-lock-face-O
225 'landmark-font-lock-face-X)))
fb7ada5f 226 "Font lock rules for Landmark.")
4d0143e6
JA
227
228(put 'landmark-mode 'front-sticky
229 (put 'landmark-mode 'rear-nonsticky '(intangible)))
230(put 'landmark-mode 'intangible 1)
c52b27c8
EZ
231;; This one is for when they set view-read-only to t: Landmark cannot
232;; allow View Mode to be activated in its buffer.
512e4cdc 233(define-derived-mode landmark-mode special-mode "Lm"
1b3b87df 234 "Major mode for playing Lm against Emacs.
178fc2d3
RS
235You and Emacs play in turn by marking a free square. You mark it with X
236and Emacs marks it with O. The winner is the first to get five contiguous
237marks horizontally, vertically or in diagonal.
238
4d0143e6 239You play by moving the cursor over the square you choose and hitting \\[landmark-human-plays].
178fc2d3
RS
240
241Other useful commands:
4d0143e6
JA
242\\{landmark-mode-map}
243Entry to this mode calls the value of `landmark-mode-hook' if that value
178fc2d3 244is non-nil. One interesting value is `turn-on-font-lock'."
512e4cdc 245 (landmark-display-statistics)
ed88649e 246 (setq-local font-lock-defaults '(landmark-font-lock-keywords t))
1b3b87df 247 (setq buffer-read-only t))
178fc2d3
RS
248
249
178fc2d3
RS
250;;;_ + THE SCORE TABLE.
251
252
253;; Every (free) square has a score associated to it, recorded in the
4d0143e6 254;; LANDMARK-SCORE-TABLE vector. The program always plays in the square having
178fc2d3
RS
255;; the highest score.
256
4d0143e6 257(defvar landmark-score-table nil
178fc2d3
RS
258 "Vector recording the actual score of the free squares.")
259
260
261;; The key point point about the algorithm is that, rather than considering
262;; the board as just a set of squares, we prefer to see it as a "space" of
263;; internested 5-tuples of contiguous squares (called qtuples).
264;;
265;; The aim of the program is to fill one qtuple with its O's while preventing
266;; you from filling another one with your X's. To that effect, it computes a
267;; score for every qtuple, with better qtuples having better scores. Of
268;; course, the score of a qtuple (taken in isolation) is just determined by
269;; its contents as a set, i.e. not considering the order of its elements. The
270;; highest score is given to the "OOOO" qtuples because playing in such a
271;; qtuple is winning the game. Just after this comes the "XXXX" qtuple because
52365e61 272;; not playing in it is just losing the game, and so on. Note that a
178fc2d3
RS
273;; "polluted" qtuple, i.e. one containing at least one X and at least one O,
274;; has score zero because there is no more any point in playing in it, from
275;; both an attacking and a defending point of view.
276;;
277;; Given the score of every qtuple, the score of a given free square on the
278;; board is just the sum of the scores of all the qtuples to which it belongs,
279;; because playing in that square is playing in all its containing qtuples at
280;; once. And it is that function which takes into account the internesting of
281;; the qtuples.
282;;
283;; This algorithm is rather simple but anyway it gives a not so dumb level of
4d0143e6 284;; play. It easily extends to "n-dimensional Landmark", where a win should not
178fc2d3
RS
285;; be obtained with as few as 5 contiguous marks: 6 or 7 (depending on n !)
286;; should be preferred.
287
288
289;; Here are the scores of the nine "non-polluted" configurations. Tuning
290;; these values will change (hopefully improve) the strength of the program
291;; and may change its style (rather aggressive here).
292
4d0143e6 293(defconst landmark-nil-score 7 "Score of an empty qtuple.")
178fc2d3 294
4d0143e6 295(defconst landmark-score-trans-table
80cb310d
SM
296 (let ((Xscore 15) ; Score of a qtuple containing one X.
297 (XXscore 400) ; Score of a qtuple containing two X's.
298 (XXXscore 1800) ; Score of a qtuple containing three X's.
299 (XXXXscore 100000) ; Score of a qtuple containing four X's.
300 (Oscore 35) ; Score of a qtuple containing one O.
301 (OOscore 800) ; Score of a qtuple containing two O's.
302 (OOOscore 15000) ; Score of a qtuple containing three O's.
303 (OOOOscore 800000)) ; Score of a qtuple containing four O's.
304
305 ;; These values are not just random: if, given the following situation:
306 ;;
307 ;; . . . . . . . O .
308 ;; . X X a . . . X .
309 ;; . . . X . . . X .
310 ;; . . . X . . . X .
311 ;; . . . . . . . b .
312 ;;
313 ;; you want Emacs to play in "a" and not in "b", then the parameters must
314 ;; satisfy the inequality:
315 ;;
316 ;; 6 * XXscore > XXXscore + XXscore
317 ;;
318 ;; because "a" mainly belongs to six "XX" qtuples (the others are less
319 ;; important) while "b" belongs to one "XXX" and one "XX" qtuples.
320 ;; Other conditions are required to obtain sensible moves, but the
321 ;; previous example should illustrate the point. If you manage to
322 ;; improve on these values, please send me a note. Thanks.
323
324
325 ;; As we chose values 0, 1 and 6 to denote empty, X and O squares,
326 ;; the contents of a qtuple are uniquely determined by the sum of
327 ;; its elements and we just have to set up a translation table.
4d0143e6 328 (vector landmark-nil-score Xscore XXscore XXXscore XXXXscore 0
80cb310d
SM
329 Oscore 0 0 0 0 0
330 OOscore 0 0 0 0 0
331 OOOscore 0 0 0 0 0
332 OOOOscore 0 0 0 0 0
333 0))
178fc2d3
RS
334 "Vector associating qtuple contents to their score.")
335
336
337;; If you do not modify drastically the previous constants, the only way for a
338;; square to have a score higher than OOOOscore is to belong to a "OOOO"
339;; qtuple, thus to be a winning move. Similarly, the only way for a square to
340;; have a score between XXXXscore and OOOOscore is to belong to a "XXXX"
341;; qtuple. We may use these considerations to detect when a given move is
52365e61 342;; winning or losing.
178fc2d3 343
4d0143e6
JA
344(defconst landmark-winning-threshold
345 (aref landmark-score-trans-table (+ 6 6 6 6)) ;; OOOOscore
178fc2d3
RS
346 "Threshold score beyond which an Emacs move is winning.")
347
4d0143e6
JA
348(defconst landmark-losing-threshold
349 (aref landmark-score-trans-table (+ 1 1 1 1)) ;; XXXXscore
178fc2d3
RS
350 "Threshold score beyond which a human move is winning.")
351
352
4d0143e6 353(defun landmark-strongest-square ()
178fc2d3
RS
354 "Compute index of free square with highest score, or nil if none."
355 ;; We just have to loop other all squares. However there are two problems:
356 ;; 1/ The SCORE-TABLE only gives correct scores to free squares. To speed
357 ;; up future searches, we set the score of padding or occupied squares
358 ;; to -1 whenever we meet them.
359 ;; 2/ We want to choose randomly between equally good moves.
360 (let ((score-max 0)
361 (count 0) ; Number of equally good moves
4d0143e6
JA
362 (square (landmark-xy-to-index 1 1)) ; First square
363 (end (landmark-xy-to-index landmark-board-width landmark-board-height))
178fc2d3
RS
364 best-square score)
365 (while (<= square end)
366 (cond
367 ;; If score is lower (i.e. most of the time), skip to next:
4d0143e6 368 ((< (aref landmark-score-table square) score-max))
178fc2d3 369 ;; If score is better, beware of non free squares:
4d0143e6
JA
370 ((> (setq score (aref landmark-score-table square)) score-max)
371 (if (zerop (aref landmark-board square)) ; is it free ?
178fc2d3
RS
372 (setq count 1 ; yes: take it !
373 best-square square
374 score-max score)
4d0143e6 375 (aset landmark-score-table square -1))) ; no: kill it !
e4769531 376 ;; If score is equally good, choose randomly. But first check freedom:
4d0143e6
JA
377 ((not (zerop (aref landmark-board square)))
378 (aset landmark-score-table square -1))
178fc2d3
RS
379 ((zerop (random (setq count (1+ count))))
380 (setq best-square square
381 score-max score)))
382 (setq square (1+ square))) ; try next square
383 best-square))
384
385;;;_ - INITIALIZING THE SCORE TABLE.
386
387;; At initialization the board is empty so that every qtuple amounts for
388;; nil-score. Therefore, the score of any square is nil-score times the number
389;; of qtuples that pass through it. This number is 3 in a corner and 20 if you
390;; are sufficiently far from the sides. As computing the number is time
391;; consuming, we initialize every square with 20*nil-score and then only
392;; consider squares at less than 5 squares from one side. We speed this up by
393;; taking symmetry into account.
394;; Also, as it is likely that successive games will be played on a board with
395;; same size, it is a good idea to save the initial SCORE-TABLE configuration.
396
4d0143e6 397(defvar landmark-saved-score-table nil
178fc2d3
RS
398 "Recorded initial value of previous score table.")
399
4d0143e6 400(defvar landmark-saved-board-width nil
178fc2d3
RS
401 "Recorded value of previous board width.")
402
4d0143e6 403(defvar landmark-saved-board-height nil
178fc2d3
RS
404 "Recorded value of previous board height.")
405
406
4d0143e6 407(defun landmark-init-score-table ()
178fc2d3 408 "Create the score table vector and fill it with initial values."
4d0143e6
JA
409 (if (and landmark-saved-score-table ; Has it been stored last time ?
410 (= landmark-board-width landmark-saved-board-width)
411 (= landmark-board-height landmark-saved-board-height))
412 (setq landmark-score-table (copy-sequence landmark-saved-score-table))
178fc2d3 413 ;; No, compute it:
4d0143e6
JA
414 (setq landmark-score-table
415 (make-vector landmark-vector-length (* 20 landmark-nil-score)))
178fc2d3 416 (let (i j maxi maxj maxi2 maxj2)
4d0143e6
JA
417 (setq maxi (/ (1+ landmark-board-width) 2)
418 maxj (/ (1+ landmark-board-height) 2)
178fc2d3
RS
419 maxi2 (min 4 maxi)
420 maxj2 (min 4 maxj))
421 ;; We took symmetry into account and could use it more if the board
422 ;; would have been square and not rectangular !
423 ;; In our case we deal with all (i,j) in the set [1..maxi2]*[1..maxj] U
424 ;; [maxi2+1..maxi]*[1..maxj2]. Maxi2 and maxj2 are used because the
425 ;; board may well be less than 8 by 8 !
426 (setq i 1)
427 (while (<= i maxi2)
428 (setq j 1)
429 (while (<= j maxj)
4d0143e6 430 (landmark-init-square-score i j)
178fc2d3
RS
431 (setq j (1+ j)))
432 (setq i (1+ i)))
433 (while (<= i maxi)
434 (setq j 1)
435 (while (<= j maxj2)
4d0143e6 436 (landmark-init-square-score i j)
178fc2d3
RS
437 (setq j (1+ j)))
438 (setq i (1+ i))))
4d0143e6
JA
439 (setq landmark-saved-score-table (copy-sequence landmark-score-table)
440 landmark-saved-board-width landmark-board-width
441 landmark-saved-board-height landmark-board-height)))
178fc2d3 442
4d0143e6 443(defun landmark-nb-qtuples (i j)
178fc2d3
RS
444 "Return the number of qtuples containing square I,J."
445 ;; This function is complicated because we have to deal
446 ;; with ugly cases like 3 by 6 boards, but it works.
447 ;; If you have a simpler (and correct) solution, send it to me. Thanks !
448 (let ((left (min 4 (1- i)))
4d0143e6 449 (right (min 4 (- landmark-board-width i)))
178fc2d3 450 (up (min 4 (1- j)))
4d0143e6 451 (down (min 4 (- landmark-board-height j))))
178fc2d3
RS
452 (+ -12
453 (min (max (+ left right) 3) 8)
454 (min (max (+ up down) 3) 8)
455 (min (max (+ (min left up) (min right down)) 3) 8)
456 (min (max (+ (min right up) (min left down)) 3) 8))))
457
4d0143e6 458(defun landmark-init-square-score (i j)
178fc2d3 459 "Give initial score to square I,J and to its mirror images."
4d0143e6
JA
460 (let ((ii (1+ (- landmark-board-width i)))
461 (jj (1+ (- landmark-board-height j)))
462 (sc (* (landmark-nb-qtuples i j) (aref landmark-score-trans-table 0))))
463 (aset landmark-score-table (landmark-xy-to-index i j) sc)
464 (aset landmark-score-table (landmark-xy-to-index ii j) sc)
465 (aset landmark-score-table (landmark-xy-to-index i jj) sc)
466 (aset landmark-score-table (landmark-xy-to-index ii jj) sc)))
178fc2d3
RS
467;;;_ - MAINTAINING THE SCORE TABLE.
468
469
470;; We do not provide functions for computing the SCORE-TABLE given the
471;; contents of the BOARD. This would involve heavy nested loops, with time
472;; proportional to the size of the board. It is better to update the
473;; SCORE-TABLE after each move. Updating needs not modify more than 36
474;; squares: it is done in constant time.
475
4d0143e6 476(defun landmark-update-score-table (square dval)
178fc2d3
RS
477 "Update score table after SQUARE received a DVAL increment."
478 ;; The board has already been updated when this function is called.
479 ;; Updating scores is done by looking for qtuples boundaries in all four
480 ;; directions and then calling update-score-in-direction.
481 ;; Finally all squares received the right increment, and then are up to
482 ;; date, except possibly for SQUARE itself if we are taking a move back for
483 ;; its score had been set to -1 at the time.
4d0143e6
JA
484 (let* ((x (landmark-index-to-x square))
485 (y (landmark-index-to-y square))
178fc2d3
RS
486 (imin (max -4 (- 1 x)))
487 (jmin (max -4 (- 1 y)))
4d0143e6
JA
488 (imax (min 0 (- landmark-board-width x 4)))
489 (jmax (min 0 (- landmark-board-height y 4))))
490 (landmark-update-score-in-direction imin imax
178fc2d3 491 square 1 0 dval)
4d0143e6 492 (landmark-update-score-in-direction jmin jmax
178fc2d3 493 square 0 1 dval)
4d0143e6 494 (landmark-update-score-in-direction (max imin jmin) (min imax jmax)
178fc2d3 495 square 1 1 dval)
4d0143e6
JA
496 (landmark-update-score-in-direction (max (- 1 y) -4
497 (- x landmark-board-width))
178fc2d3 498 (min 0 (- x 5)
4d0143e6 499 (- landmark-board-height y 4))
178fc2d3
RS
500 square -1 1 dval)))
501
4d0143e6 502(defun landmark-update-score-in-direction (left right square dx dy dval)
a7b88742
KH
503 "Update scores for all squares in the qtuples in range.
504That is, those between the LEFTth square and the RIGHTth after SQUARE,
505along the DX, DY direction, considering that DVAL has been added on SQUARE."
178fc2d3
RS
506 ;; We always have LEFT <= 0, RIGHT <= 0 and DEPL > 0 but we may very well
507 ;; have LEFT > RIGHT, indicating that no qtuple contains SQUARE along that
508 ;; DX,DY direction.
509 (cond
510 ((> left right)) ; Quit
511 (t ; Else ..
512 (let (depl square0 square1 square2 count delta)
4d0143e6 513 (setq depl (landmark-xy-to-index dx dy)
178fc2d3
RS
514 square0 (+ square (* left depl))
515 square1 (+ square (* right depl))
516 square2 (+ square0 (* 4 depl)))
517 ;; Compute the contents of the first qtuple:
518 (setq square square0
519 count 0)
520 (while (<= square square2)
4d0143e6 521 (setq count (+ count (aref landmark-board square))
178fc2d3
RS
522 square (+ square depl)))
523 (while (<= square0 square1)
524 ;; Update the squares of the qtuple beginning in SQUARE0 and ending
525 ;; in SQUARE2.
4d0143e6
JA
526 (setq delta (- (aref landmark-score-trans-table count)
527 (aref landmark-score-trans-table (- count dval))))
178fc2d3
RS
528 (cond ((not (zerop delta)) ; or else nothing to update
529 (setq square square0)
530 (while (<= square square2)
4d0143e6
JA
531 (if (zerop (aref landmark-board square)) ; only for free squares
532 (aset landmark-score-table square
533 (+ (aref landmark-score-table square) delta)))
178fc2d3
RS
534 (setq square (+ square depl)))))
535 ;; Then shift the qtuple one square along DEPL, this only requires
536 ;; modifying SQUARE0 and SQUARE2.
537 (setq square2 (+ square2 depl)
4d0143e6
JA
538 count (+ count (- (aref landmark-board square0))
539 (aref landmark-board square2))
178fc2d3
RS
540 square0 (+ square0 depl)))))))
541
542;;;
543;;; GAME CONTROL.
544;;;
545
546;; Several variables are used to monitor a game, including a GAME-HISTORY (the
547;; list of all (SQUARE . PREVSCORE) played) that allows to take moves back
548;; (anti-updating the score table) and to compute the table from scratch in
549;; case of an interruption.
550
4d0143e6 551(defvar landmark-game-in-progress nil
178fc2d3
RS
552 "Non-nil if a game is in progress.")
553
4d0143e6 554(defvar landmark-game-history nil
178fc2d3
RS
555 "A record of all moves that have been played during current game.")
556
4d0143e6 557(defvar landmark-number-of-moves nil
178fc2d3
RS
558 "Number of moves already played in current game.")
559
4d0143e6 560(defvar landmark-number-of-human-moves nil
178fc2d3
RS
561 "Number of moves already played by human in current game.")
562
4d0143e6 563(defvar landmark-emacs-played-first nil
178fc2d3
RS
564 "Non-nil if Emacs played first.")
565
4d0143e6 566(defvar landmark-human-took-back nil
178fc2d3
RS
567 "Non-nil if Human took back a move during the game.")
568
4d0143e6 569(defvar landmark-human-refused-draw nil
178fc2d3
RS
570 "Non-nil if Human refused Emacs offer of a draw.")
571
4d0143e6 572(defvar landmark-emacs-is-computing nil
178fc2d3
RS
573 ;; This is used to detect interruptions. Hopefully, it should not be needed.
574 "Non-nil if Emacs is in the middle of a computation.")
575
576
4d0143e6 577(defun landmark-start-game (n m)
178fc2d3 578 "Initialize a new game on an N by M board."
4d0143e6
JA
579 (setq landmark-emacs-is-computing t) ; Raise flag
580 (setq landmark-game-in-progress t)
581 (setq landmark-board-width n
582 landmark-board-height m
583 landmark-vector-length (1+ (* (+ m 2) (1+ n)))
584 landmark-draw-limit (/ (* 7 n m) 10))
585 (setq landmark-emacs-won nil
586 landmark-game-history nil
587 landmark-number-of-moves 0
588 landmark-number-of-human-moves 0
589 landmark-emacs-played-first nil
590 landmark-human-took-back nil
591 landmark-human-refused-draw nil)
592 (landmark-init-display n m) ; Display first: the rest takes time
593 (landmark-init-score-table) ; INIT-BOARD requires that the score
594 (landmark-init-board) ; table be already created.
595 (setq landmark-emacs-is-computing nil))
596
597(defun landmark-play-move (square val &optional dont-update-score)
178fc2d3 598 "Go to SQUARE, play VAL and update everything."
4d0143e6 599 (setq landmark-emacs-is-computing t) ; Raise flag
178fc2d3 600 (cond ((= 1 val) ; a Human move
4d0143e6
JA
601 (setq landmark-number-of-human-moves (1+ landmark-number-of-human-moves)))
602 ((zerop landmark-number-of-moves) ; an Emacs move. Is it first ?
603 (setq landmark-emacs-played-first t)))
604 (setq landmark-game-history
605 (cons (cons square (aref landmark-score-table square))
606 landmark-game-history)
607 landmark-number-of-moves (1+ landmark-number-of-moves))
608 (landmark-plot-square square val)
609 (aset landmark-board square val) ; *BEFORE* UPDATE-SCORE !
178fc2d3 610 (if dont-update-score nil
4d0143e6
JA
611 (landmark-update-score-table square val) ; previous val was 0: dval = val
612 (aset landmark-score-table square -1))
613 (setq landmark-emacs-is-computing nil))
178fc2d3 614
4d0143e6 615(defun landmark-take-back ()
178fc2d3 616 "Take back last move and update everything."
4d0143e6
JA
617 (setq landmark-emacs-is-computing t)
618 (let* ((last-move (car landmark-game-history))
178fc2d3 619 (square (car last-move))
4d0143e6 620 (oldval (aref landmark-board square)))
178fc2d3 621 (if (= 1 oldval)
4d0143e6
JA
622 (setq landmark-number-of-human-moves (1- landmark-number-of-human-moves)))
623 (setq landmark-game-history (cdr landmark-game-history)
624 landmark-number-of-moves (1- landmark-number-of-moves))
625 (landmark-plot-square square 0)
626 (aset landmark-board square 0) ; *BEFORE* UPDATE-SCORE !
627 (landmark-update-score-table square (- oldval))
628 (aset landmark-score-table square (cdr last-move)))
629 (setq landmark-emacs-is-computing nil))
178fc2d3
RS
630
631
178fc2d3
RS
632;;;_ + SESSION CONTROL.
633
4d0143e6 634(defvar landmark-number-of-trials 0
a7b88742
KH
635 "The number of times that landmark has been run.")
636
4d0143e6 637(defvar landmark-sum-of-moves 0
a7b88742 638 "The total number of moves made in all games.")
178fc2d3 639
4d0143e6 640(defvar landmark-number-of-emacs-wins 0
178fc2d3
RS
641 "Number of games Emacs won in this session.")
642
4d0143e6 643(defvar landmark-number-of-human-wins 0
178fc2d3
RS
644 "Number of games you won in this session.")
645
4d0143e6 646(defvar landmark-number-of-draws 0
178fc2d3
RS
647 "Number of games already drawn in this session.")
648
649
4d0143e6 650(defun landmark-terminate-game (result)
178fc2d3 651 "Terminate the current game with RESULT."
4d0143e6
JA
652 (setq landmark-number-of-trials (1+ landmark-number-of-trials))
653 (setq landmark-sum-of-moves (+ landmark-sum-of-moves landmark-number-of-moves))
a7b88742
KH
654 (if (eq result 'crash-game)
655 (message
656 "Sorry, I have been interrupted and cannot resume that game..."))
4d0143e6 657 (landmark-display-statistics)
178fc2d3 658 ;;(ding)
4d0143e6 659 (setq landmark-game-in-progress nil))
178fc2d3 660
4d0143e6 661(defun landmark-crash-game ()
178fc2d3 662 "What to do when Emacs detects it has been interrupted."
4d0143e6
JA
663 (setq landmark-emacs-is-computing nil)
664 (landmark-terminate-game 'crash-game)
178fc2d3 665 (sit-for 4) ; Let's see the message
4d0143e6 666 (landmark-prompt-for-other-game))
178fc2d3
RS
667
668
178fc2d3
RS
669;;;_ + INTERACTIVE COMMANDS.
670
4d0143e6 671(defun landmark-emacs-plays ()
178fc2d3
RS
672 "Compute Emacs next move and play it."
673 (interactive)
4d0143e6 674 (landmark-switch-to-window)
178fc2d3 675 (cond
4d0143e6
JA
676 (landmark-emacs-is-computing
677 (landmark-crash-game))
678 ((not landmark-game-in-progress)
679 (landmark-prompt-for-other-game))
178fc2d3
RS
680 (t
681 (message "Let me think...")
682 (let (square score)
4d0143e6 683 (setq square (landmark-strongest-square))
178fc2d3 684 (cond ((null square)
4d0143e6 685 (landmark-terminate-game 'nobody-won))
178fc2d3 686 (t
4d0143e6
JA
687 (setq score (aref landmark-score-table square))
688 (landmark-play-move square 6)
689 (cond ((>= score landmark-winning-threshold)
690 (setq landmark-emacs-won t) ; for font-lock
691 (landmark-find-filled-qtuple square 6)
692 (landmark-terminate-game 'emacs-won))
178fc2d3 693 ((zerop score)
4d0143e6
JA
694 (landmark-terminate-game 'nobody-won))
695 ((and (> landmark-number-of-moves landmark-draw-limit)
696 (not landmark-human-refused-draw)
697 (landmark-offer-a-draw))
698 (landmark-terminate-game 'draw-agreed))
178fc2d3 699 (t
4d0143e6 700 (landmark-prompt-for-move)))))))))
178fc2d3
RS
701
702;; For small square dimensions this is approximate, since though measured in
703;; pixels, event's (X . Y) is a character's top-left corner.
4d0143e6 704(defun landmark-click (click)
178fc2d3
RS
705 "Position at the square where you click."
706 (interactive "e")
707 (and (windowp (posn-window (setq click (event-end click))))
708 (numberp (posn-point click))
709 (select-window (posn-window click))
710 (setq click (posn-col-row click))
4d0143e6 711 (landmark-goto-xy
178fc2d3 712 (min (max (/ (+ (- (car click)
4d0143e6 713 landmark-x-offset
178fc2d3
RS
714 1)
715 (window-hscroll)
4d0143e6
JA
716 landmark-square-width
717 (% landmark-square-width 2)
718 (/ landmark-square-width 2))
719 landmark-square-width)
178fc2d3 720 1)
4d0143e6 721 landmark-board-width)
178fc2d3 722 (min (max (/ (+ (- (cdr click)
4d0143e6 723 landmark-y-offset
178fc2d3
RS
724 1)
725 (let ((inhibit-point-motion-hooks t))
726 (count-lines 1 (window-start)))
4d0143e6
JA
727 landmark-square-height
728 (% landmark-square-height 2)
729 (/ landmark-square-height 2))
730 landmark-square-height)
178fc2d3 731 1)
4d0143e6 732 landmark-board-height))))
a7b88742 733
4d0143e6 734(defun landmark-mouse-play (click)
178fc2d3
RS
735 "Play at the square where you click."
736 (interactive "e")
4d0143e6
JA
737 (if (landmark-click click)
738 (landmark-human-plays)))
178fc2d3 739
4d0143e6
JA
740(defun landmark-human-plays ()
741 "Signal to the Landmark program that you have played.
178fc2d3
RS
742You must have put the cursor on the square where you want to play.
743If the game is finished, this command requests for another game."
744 (interactive)
4d0143e6 745 (landmark-switch-to-window)
178fc2d3 746 (cond
4d0143e6
JA
747 (landmark-emacs-is-computing
748 (landmark-crash-game))
749 ((not landmark-game-in-progress)
750 (landmark-prompt-for-other-game))
178fc2d3
RS
751 (t
752 (let (square score)
4d0143e6 753 (setq square (landmark-point-square))
178fc2d3 754 (cond ((null square)
ce5a3ac0 755 (error "Your point is not on a square. Retry!"))
4d0143e6 756 ((not (zerop (aref landmark-board square)))
ce5a3ac0 757 (error "Your point is not on a free square. Retry!"))
178fc2d3 758 (t
4d0143e6
JA
759 (setq score (aref landmark-score-table square))
760 (landmark-play-move square 1)
761 (cond ((and (>= score landmark-losing-threshold)
178fc2d3
RS
762 ;; Just testing SCORE > THRESHOLD is not enough for
763 ;; detecting wins, it just gives an indication that
4d0143e6
JA
764 ;; we confirm with LANDMARK-FIND-FILLED-QTUPLE.
765 (landmark-find-filled-qtuple square 1))
766 (landmark-terminate-game 'human-won))
178fc2d3 767 (t
4d0143e6 768 (landmark-emacs-plays)))))))))
178fc2d3 769
4d0143e6
JA
770(defun landmark-human-takes-back ()
771 "Signal to the Landmark program that you wish to take back your last move."
178fc2d3 772 (interactive)
4d0143e6 773 (landmark-switch-to-window)
178fc2d3 774 (cond
4d0143e6
JA
775 (landmark-emacs-is-computing
776 (landmark-crash-game))
777 ((not landmark-game-in-progress)
178fc2d3
RS
778 (message "Too late for taking back...")
779 (sit-for 4)
4d0143e6
JA
780 (landmark-prompt-for-other-game))
781 ((zerop landmark-number-of-human-moves)
ce5a3ac0 782 (message "You have not played yet... Your move?"))
178fc2d3
RS
783 (t
784 (message "One moment, please...")
785 ;; It is possible for the user to let Emacs play several consecutive
786 ;; moves, so that the best way to know when to stop taking back moves is
787 ;; to count the number of human moves:
4d0143e6
JA
788 (setq landmark-human-took-back t)
789 (let ((number landmark-number-of-human-moves))
790 (while (= number landmark-number-of-human-moves)
791 (landmark-take-back)))
792 (landmark-prompt-for-move))))
793
794(defun landmark-human-resigns ()
795 "Signal to the Landmark program that you may want to resign."
178fc2d3 796 (interactive)
4d0143e6 797 (landmark-switch-to-window)
178fc2d3 798 (cond
4d0143e6
JA
799 (landmark-emacs-is-computing
800 (landmark-crash-game))
801 ((not landmark-game-in-progress)
178fc2d3 802 (message "There is no game in progress"))
ce5a3ac0 803 ((y-or-n-p "You mean, you resign? ")
4d0143e6 804 (landmark-terminate-game 'human-resigned))
ce5a3ac0 805 ((y-or-n-p "You mean, we continue? ")
4d0143e6 806 (landmark-prompt-for-move))
178fc2d3 807 (t
4d0143e6 808 (landmark-terminate-game 'human-resigned)))) ; OK. Accept it
178fc2d3 809
178fc2d3
RS
810;;;_ + PROMPTING THE HUMAN PLAYER.
811
4d0143e6 812(defun landmark-prompt-for-move ()
178fc2d3 813 "Display a message asking for Human's move."
4d0143e6 814 (message (if (zerop landmark-number-of-human-moves)
ce5a3ac0 815 "Your move? (move to a free square and hit X, RET ...)"
55a4b4fe 816 "Your move?")))
178fc2d3 817
4d0143e6 818(defun landmark-prompt-for-other-game ()
178fc2d3 819 "Ask for another game, and start it."
ce5a3ac0 820 (if (y-or-n-p "Another game? ")
a7b88742 821 (if (y-or-n-p "Retain learned weights ")
4d0143e6
JA
822 (landmark 2)
823 (landmark 1))
ce5a3ac0 824 (message "Chicken!")))
178fc2d3 825
4d0143e6 826(defun landmark-offer-a-draw ()
a7b88742 827 "Offer a draw and return t if Human accepted it."
ce5a3ac0 828 (or (y-or-n-p "I offer you a draw. Do you accept it? ")
4d0143e6 829 (not (setq landmark-human-refused-draw t))))
178fc2d3
RS
830
831
4d0143e6 832(defun landmark-max-width ()
178fc2d3 833 "Largest possible board width for the current window."
290d5b58 834 (1+ (/ (- (window-width)
4d0143e6
JA
835 landmark-x-offset landmark-x-offset 1)
836 landmark-square-width)))
178fc2d3 837
4d0143e6 838(defun landmark-max-height ()
178fc2d3 839 "Largest possible board height for the current window."
290d5b58 840 (1+ (/ (- (window-height)
4d0143e6 841 landmark-y-offset landmark-y-offset 2)
178fc2d3 842 ;; 2 instead of 1 because WINDOW-HEIGHT includes the mode line !
4d0143e6 843 landmark-square-height)))
178fc2d3 844
4d0143e6 845(defun landmark-point-y ()
178fc2d3
RS
846 "Return the board row where point is."
847 (let ((inhibit-point-motion-hooks t))
4d0143e6
JA
848 (1+ (/ (- (count-lines 1 (point)) landmark-y-offset (if (bolp) 0 1))
849 landmark-square-height))))
178fc2d3 850
4d0143e6 851(defun landmark-point-square ()
178fc2d3
RS
852 "Return the index of the square point is on."
853 (let ((inhibit-point-motion-hooks t))
4d0143e6
JA
854 (landmark-xy-to-index (1+ (/ (- (current-column) landmark-x-offset)
855 landmark-square-width))
856 (landmark-point-y))))
178fc2d3 857
4d0143e6 858(defun landmark-goto-square (index)
178fc2d3 859 "Move point to square number INDEX."
4d0143e6 860 (landmark-goto-xy (landmark-index-to-x index) (landmark-index-to-y index)))
178fc2d3 861
4d0143e6 862(defun landmark-goto-xy (x y)
178fc2d3
RS
863 "Move point to square at X, Y coords."
864 (let ((inhibit-point-motion-hooks t))
9b4c5ecd 865 (goto-char (point-min))
4d0143e6
JA
866 (forward-line (+ landmark-y-offset (* landmark-square-height (1- y)))))
867 (move-to-column (+ landmark-x-offset (* landmark-square-width (1- x)))))
178fc2d3 868
4d0143e6 869(defun landmark-plot-square (square value)
178fc2d3
RS
870 "Draw 'X', 'O' or '.' on SQUARE depending on VALUE, leave point there."
871 (or (= value 1)
4d0143e6 872 (landmark-goto-square square))
178fc2d3
RS
873 (let ((inhibit-read-only t)
874 (inhibit-point-motion-hooks t))
875 (insert-and-inherit (cond ((= value 1) ?.)
876 ((= value 2) ?N)
877 ((= value 3) ?S)
878 ((= value 4) ?E)
879 ((= value 5) ?W)
880 ((= value 6) ?^)))
881
ec45b7bb 882 (and (zerop value)
366be0de
EZ
883 (add-text-properties (1- (point)) (point)
884 '(mouse-face highlight
885 help-echo "\
886mouse-1: get robot moving, mouse-2: play on this square")))
178fc2d3
RS
887 (delete-char 1)
888 (backward-char 1))
889 (sit-for 0)) ; Display NOW
890
4d0143e6
JA
891(defun landmark-init-display (n m)
892 "Display an N by M Landmark board."
178fc2d3
RS
893 (buffer-disable-undo (current-buffer))
894 (let ((inhibit-read-only t)
895 (point 1) opoint
896 (intangible t)
897 (i m) j x)
898 ;; Try to minimize number of chars (because of text properties)
899 (setq tab-width
4d0143e6
JA
900 (if (zerop (% landmark-x-offset landmark-square-width))
901 landmark-square-width
902 (max (/ (+ (% landmark-x-offset landmark-square-width)
903 landmark-square-width 1) 2) 2)))
178fc2d3 904 (erase-buffer)
4d0143e6 905 (newline landmark-y-offset)
178fc2d3
RS
906 (while (progn
907 (setq j n
4d0143e6 908 x (- landmark-x-offset landmark-square-width))
178fc2d3 909 (while (>= (setq j (1- j)) 0)
4d0143e6 910 (insert-char ?\t (/ (- (setq x (+ x landmark-square-width))
178fc2d3
RS
911 (current-column))
912 tab-width))
913 (insert-char ? (- x (current-column)))
914 (if (setq intangible (not intangible))
915 (put-text-property point (point) 'intangible 2))
916 (and (zerop j)
917 (= i (- m 2))
918 (progn
919 (while (>= i 3)
920 (append-to-buffer (current-buffer) opoint (point))
921 (setq i (- i 2)))
922 (goto-char (point-max))))
923 (setq point (point))
924 (insert ?=)
366be0de
EZ
925 (add-text-properties point (point)
926 '(mouse-face highlight help-echo "\
927mouse-1: get robot moving, mouse-2: play on this square")))
178fc2d3
RS
928 (> (setq i (1- i)) 0))
929 (if (= i (1- m))
930 (setq opoint point))
4d0143e6 931 (insert-char ?\n landmark-square-height))
178fc2d3
RS
932 (or (eq (char-after 1) ?.)
933 (put-text-property 1 2 'point-entered
121656e9 934 (lambda (_x _y) (if (bobp) (forward-char)))))
178fc2d3
RS
935 (or intangible
936 (put-text-property point (point) 'intangible 2))
937 (put-text-property point (point) 'point-entered
121656e9 938 (lambda (_x _y) (if (eobp) (backward-char))))
4d0143e6
JA
939 (put-text-property (point-min) (point) 'category 'landmark-mode))
940 (landmark-goto-xy (/ (1+ n) 2) (/ (1+ m) 2)) ; center of the board
178fc2d3
RS
941 (sit-for 0)) ; Display NOW
942
4d0143e6 943(defun landmark-display-statistics ()
178fc2d3
RS
944 "Obnoxiously display some statistics about previous games in mode line."
945 ;; We store this string in the mode-line-process local variable.
946 ;; This is certainly not the cleanest way out ...
947 (setq mode-line-process
a7b88742 948 (format ": Trials: %d, Avg#Moves: %d"
4d0143e6
JA
949 landmark-number-of-trials
950 (if (zerop landmark-number-of-trials)
a7b88742 951 0
4d0143e6 952 (/ landmark-sum-of-moves landmark-number-of-trials))))
178fc2d3
RS
953 (force-mode-line-update))
954
4d0143e6
JA
955(defun landmark-switch-to-window ()
956 "Find or create the Landmark buffer, and display it."
178fc2d3 957 (interactive)
4d0143e6 958 (let ((buff (get-buffer "*Landmark*")))
178fc2d3
RS
959 (if buff ; Buffer exists:
960 (switch-to-buffer buff) ; no problem.
4d0143e6
JA
961 (if landmark-game-in-progress
962 (landmark-crash-game)) ; buffer has been killed or something
963 (switch-to-buffer "*Landmark*") ; Anyway, start anew.
964 (landmark-mode))))
178fc2d3
RS
965
966
178fc2d3
RS
967;;;_ + CROSSING WINNING QTUPLES.
968
969;; When someone succeeds in filling a qtuple, we draw a line over the five
970;; corresponding squares. One problem is that the program does not know which
971;; squares ! It only knows the square where the last move has been played and
972;; who won. The solution is to scan the board along all four directions.
973
4d0143e6 974(defun landmark-find-filled-qtuple (square value)
a7b88742 975 "Return t if SQUARE belongs to a qtuple filled with VALUEs."
4d0143e6
JA
976 (or (landmark-check-filled-qtuple square value 1 0)
977 (landmark-check-filled-qtuple square value 0 1)
978 (landmark-check-filled-qtuple square value 1 1)
979 (landmark-check-filled-qtuple square value -1 1)))
178fc2d3 980
4d0143e6 981(defun landmark-check-filled-qtuple (square value dx dy)
a7b88742 982 "Return t if SQUARE belongs to a qtuple filled with VALUEs along DX, DY."
178fc2d3
RS
983 (let ((a 0) (b 0)
984 (left square) (right square)
4d0143e6 985 (depl (landmark-xy-to-index dx dy)))
178fc2d3 986 (while (and (> a -4) ; stretch tuple left
4d0143e6 987 (= value (aref landmark-board (setq left (- left depl)))))
178fc2d3
RS
988 (setq a (1- a)))
989 (while (and (< b (+ a 4)) ; stretch tuple right
4d0143e6 990 (= value (aref landmark-board (setq right (+ right depl)))))
178fc2d3
RS
991 (setq b (1+ b)))
992 (cond ((= b (+ a 4)) ; tuple length = 5 ?
4d0143e6 993 (landmark-cross-qtuple (+ square (* a depl)) (+ square (* b depl))
178fc2d3
RS
994 dx dy)
995 t))))
996
4d0143e6 997(defun landmark-cross-qtuple (square1 square2 dx dy)
178fc2d3
RS
998 "Cross every square between SQUARE1 and SQUARE2 in the DX, DY direction."
999 (save-excursion ; Not moving point from last square
4d0143e6 1000 (let ((depl (landmark-xy-to-index dx dy))
178fc2d3
RS
1001 (inhibit-read-only t)
1002 (inhibit-point-motion-hooks t))
1003 ;; WARNING: this function assumes DEPL > 0 and SQUARE2 > SQUARE1
1004 (while (/= square1 square2)
4d0143e6 1005 (landmark-goto-square square1)
178fc2d3
RS
1006 (setq square1 (+ square1 depl))
1007 (cond
1008 ((= dy 0) ; Horizontal
1009 (forward-char 1)
4d0143e6 1010 (insert-char ?- (1- landmark-square-width) t)
178fc2d3
RS
1011 (delete-region (point) (progn
1012 (skip-chars-forward " \t")
1013 (point))))
1014 ((= dx 0) ; Vertical
4d0143e6 1015 (let ((landmark-n 1)
178fc2d3 1016 (column (current-column)))
4d0143e6
JA
1017 (while (< landmark-n landmark-square-height)
1018 (setq landmark-n (1+ landmark-n))
178fc2d3
RS
1019 (forward-line 1)
1020 (indent-to column)
1021 (insert-and-inherit ?|))))
1022 ((= dx -1) ; 1st Diagonal
4d0143e6
JA
1023 (indent-to (prog1 (- (current-column) (/ landmark-square-width 2))
1024 (forward-line (/ landmark-square-height 2))))
178fc2d3
RS
1025 (insert-and-inherit ?/))
1026 (t ; 2nd Diagonal
4d0143e6
JA
1027 (indent-to (prog1 (+ (current-column) (/ landmark-square-width 2))
1028 (forward-line (/ landmark-square-height 2))))
178fc2d3
RS
1029 (insert-and-inherit ?\\))))))
1030 (sit-for 0)) ; Display NOW
1031
1032
178fc2d3
RS
1033;;;_ + CURSOR MOTION.
1034
1035;; previous-line and next-line don't work right with intangible newlines
4d0143e6
JA
1036(defun landmark-move-down ()
1037 "Move point down one row on the Landmark board."
178fc2d3 1038 (interactive)
4d0143e6 1039 (if (< (landmark-point-y) landmark-board-height)
6f110781
GM
1040 (let ((col (current-column)))
1041 (forward-line 1) ;;; landmark-square-height
1042 (move-to-column col))))
178fc2d3 1043
4d0143e6
JA
1044(defun landmark-move-up ()
1045 "Move point up one row on the Landmark board."
178fc2d3 1046 (interactive)
4d0143e6 1047 (if (> (landmark-point-y) 1)
6f110781
GM
1048 (let ((col (current-column)))
1049 (forward-line (- landmark-square-height))
1050 (move-to-column col))))
178fc2d3 1051
4d0143e6
JA
1052(defun landmark-move-ne ()
1053 "Move point North East on the Landmark board."
178fc2d3 1054 (interactive)
4d0143e6 1055 (landmark-move-up)
178fc2d3
RS
1056 (forward-char))
1057
4d0143e6
JA
1058(defun landmark-move-se ()
1059 "Move point South East on the Landmark board."
178fc2d3 1060 (interactive)
4d0143e6 1061 (landmark-move-down)
178fc2d3
RS
1062 (forward-char))
1063
4d0143e6
JA
1064(defun landmark-move-nw ()
1065 "Move point North West on the Landmark board."
178fc2d3 1066 (interactive)
4d0143e6 1067 (landmark-move-up)
178fc2d3
RS
1068 (backward-char))
1069
4d0143e6
JA
1070(defun landmark-move-sw ()
1071 "Move point South West on the Landmark board."
178fc2d3 1072 (interactive)
4d0143e6 1073 (landmark-move-down)
178fc2d3
RS
1074 (backward-char))
1075
4d0143e6
JA
1076(defun landmark-beginning-of-line ()
1077 "Move point to first square on the Landmark board row."
178fc2d3 1078 (interactive)
4d0143e6 1079 (move-to-column landmark-x-offset))
178fc2d3 1080
4d0143e6
JA
1081(defun landmark-end-of-line ()
1082 "Move point to last square on the Landmark board row."
178fc2d3 1083 (interactive)
4d0143e6
JA
1084 (move-to-column (+ landmark-x-offset
1085 (* landmark-square-width (1- landmark-board-width)))))
178fc2d3 1086
178fc2d3 1087
a7b88742 1088;;;_ + Simulation variables
178fc2d3 1089
4d0143e6
JA
1090;;;_ - landmark-nvar
1091(defvar landmark-nvar 0.0075
a7b88742
KH
1092 "Not used.
1093Affects a noise generator which was used in an earlier incarnation of
1094this program to add a random element to the way moves were made.")
1095;;;_ - lists of cardinal directions
1096;;;_ :
4d0143e6 1097(defvar landmark-ns '(landmark-n landmark-s)
a7b88742 1098 "Used when doing something relative to the north and south axes.")
4d0143e6 1099(defvar landmark-ew '(landmark-e landmark-w)
a7b88742 1100 "Used when doing something relative to the east and west axes.")
4d0143e6 1101(defvar landmark-directions '(landmark-n landmark-s landmark-e landmark-w)
a7b88742 1102 "The cardinal directions.")
4d0143e6
JA
1103(defvar landmark-8-directions
1104 '((landmark-n) (landmark-n landmark-w) (landmark-w) (landmark-s landmark-w)
1105 (landmark-s) (landmark-s landmark-e) (landmark-e) (landmark-n landmark-e))
a7b88742
KH
1106 "The full 8 possible directions.")
1107
4d0143e6 1108(defvar landmark-number-of-moves
a7b88742 1109 "The number of moves made by the robot so far.")
178fc2d3
RS
1110
1111
1112;;;_* Terry's mods to create lm.el
1113
4d0143e6
JA
1114;;;(setq landmark-debug nil)
1115(defvar landmark-debug nil
a7b88742 1116 "If non-nil, debugging is printed.")
4d0143e6 1117(defcustom landmark-one-moment-please nil
a7b88742
KH
1118 "If non-nil, print \"One moment please\" when a new board is generated.
1119The drawback of this is you don't see how many moves the last run took
323f7c49
SE
1120because it is overwritten by \"One moment please\"."
1121 :type 'boolean
4d0143e6
JA
1122 :group 'landmark)
1123(defcustom landmark-output-moves t
323f7c49
SE
1124 "If non-nil, output number of moves so far on a move-by-move basis."
1125 :type 'boolean
4d0143e6 1126 :group 'landmark)
178fc2d3 1127
178fc2d3 1128
4d0143e6
JA
1129(defun landmark-weights-debug ()
1130 (if landmark-debug
1131 (progn (landmark-print-wts) (landmark-blackbox) (landmark-print-y-s-noise)
1132 (landmark-print-smell))))
178fc2d3
RS
1133
1134;;;_ - Printing various things
4d0143e6 1135(defun landmark-print-distance-int (direction)
178fc2d3
RS
1136 (interactive)
1137 (insert (format "%S %S " direction (get direction 'distance))))
1138
1139
4d0143e6
JA
1140(defun landmark-print-distance ()
1141 (insert (format "tree: %S \n" (calc-distance-of-robot-from 'landmark-tree)))
1142 (mapc 'landmark-print-distance-int landmark-directions))
178fc2d3
RS
1143
1144
4d0143e6
JA
1145;;(setq direction 'landmark-n)
1146;;(get 'landmark-n 'landmark-s)
1147(defun landmark-nslify-wts-int (direction)
c0df1972 1148 (mapcar (lambda (target-direction)
178fc2d3 1149 (get direction target-direction))
4d0143e6 1150 landmark-directions))
178fc2d3
RS
1151
1152
4d0143e6 1153(defun landmark-nslify-wts ()
178fc2d3 1154 (interactive)
4d0143e6 1155 (let ((l (apply 'append (mapcar 'landmark-nslify-wts-int landmark-directions))))
178fc2d3
RS
1156 (insert (format "set data_value WTS \n %s \n" l))
1157 (insert (format "/* max: %S min: %S */"
1158 (eval (cons 'max l)) (eval (cons 'min l))))))
1159
4d0143e6 1160(defun landmark-print-wts-int (direction)
c0df1972 1161 (mapc (lambda (target-direction)
178fc2d3
RS
1162 (insert (format "%S %S %S "
1163 direction
1164 target-direction
1165 (get direction target-direction))))
4d0143e6 1166 landmark-directions)
178fc2d3
RS
1167 (insert "\n"))
1168
4d0143e6 1169(defun landmark-print-wts ()
178fc2d3 1170 (interactive)
4d0143e6 1171 (with-current-buffer "*landmark-wts*"
178fc2d3 1172 (insert "==============================\n")
4d0143e6 1173 (mapc 'landmark-print-wts-int landmark-directions)))
178fc2d3 1174
4d0143e6 1175(defun landmark-print-moves (moves)
178fc2d3 1176 (interactive)
4d0143e6 1177 (with-current-buffer "*landmark-moves*"
178fc2d3
RS
1178 (insert (format "%S\n" moves))))
1179
1180
4d0143e6
JA
1181(defun landmark-print-y-s-noise-int (direction)
1182 (insert (format "%S:landmark-y %S, s %S, noise %S \n"
178fc2d3
RS
1183 (symbol-name direction)
1184 (get direction 'y_t)
1185 (get direction 's)
1186 (get direction 'noise)
1187 )))
1188
4d0143e6 1189(defun landmark-print-y-s-noise ()
178fc2d3 1190 (interactive)
4d0143e6 1191 (with-current-buffer "*landmark-y,s,noise*"
178fc2d3 1192 (insert "==============================\n")
4d0143e6 1193 (mapc 'landmark-print-y-s-noise-int landmark-directions)))
178fc2d3 1194
4d0143e6 1195(defun landmark-print-smell-int (direction)
178fc2d3
RS
1196 (insert (format "%S: smell: %S \n"
1197 (symbol-name direction)
1198 (get direction 'smell))))
1199
4d0143e6 1200(defun landmark-print-smell ()
178fc2d3 1201 (interactive)
4d0143e6 1202 (with-current-buffer "*landmark-smell*"
178fc2d3
RS
1203 (insert "==============================\n")
1204 (insert (format "tree: %S \n" (get 'z 't)))
4d0143e6 1205 (mapc 'landmark-print-smell-int landmark-directions)))
178fc2d3 1206
4d0143e6 1207(defun landmark-print-w0-int (direction)
178fc2d3
RS
1208 (insert (format "%S: w0: %S \n"
1209 (symbol-name direction)
1210 (get direction 'w0))))
1211
4d0143e6 1212(defun landmark-print-w0 ()
178fc2d3 1213 (interactive)
4d0143e6 1214 (with-current-buffer "*landmark-w0*"
178fc2d3 1215 (insert "==============================\n")
4d0143e6 1216 (mapc 'landmark-print-w0-int landmark-directions)))
178fc2d3 1217
4d0143e6
JA
1218(defun landmark-blackbox ()
1219 (with-current-buffer "*landmark-blackbox*"
178fc2d3
RS
1220 (insert "==============================\n")
1221 (insert "I smell: ")
c0df1972 1222 (mapc (lambda (direction)
178fc2d3
RS
1223 (if (> (get direction 'smell) 0)
1224 (insert (format "%S " direction))))
4d0143e6 1225 landmark-directions)
178fc2d3
RS
1226 (insert "\n")
1227
1228 (insert "I move: ")
c0df1972 1229 (mapc (lambda (direction)
178fc2d3
RS
1230 (if (> (get direction 'y_t) 0)
1231 (insert (format "%S " direction))))
4d0143e6 1232 landmark-directions)
178fc2d3 1233 (insert "\n")
4d0143e6 1234 (landmark-print-wts-blackbox)
178fc2d3 1235 (insert (format "z_t-z_t-1: %S" (- (get 'z 't) (get 'z 't-1))))
4d0143e6 1236 (landmark-print-distance)
178fc2d3
RS
1237 (insert "\n")))
1238
4d0143e6 1239(defun landmark-print-wts-blackbox ()
178fc2d3 1240 (interactive)
4d0143e6 1241 (mapc 'landmark-print-wts-int landmark-directions))
178fc2d3 1242
178fc2d3 1243;;;_ - learning parameters
4d0143e6 1244(defcustom landmark-bound 0.005
323f7c49
SE
1245 "The maximum that w0j may be."
1246 :type 'number
4d0143e6
JA
1247 :group 'landmark)
1248(defcustom landmark-c 1.0
a7b88742 1249 "A factor applied to modulate the increase in wij.
4d0143e6 1250Used in the function landmark-update-normal-weights."
323f7c49 1251 :type 'number
4d0143e6
JA
1252 :group 'landmark)
1253(defcustom landmark-c-naught 0.5
a7b88742 1254 "A factor applied to modulate the increase in w0j.
4d0143e6 1255Used in the function landmark-update-naught-weights."
323f7c49 1256 :type 'number
4d0143e6
JA
1257 :group 'landmark)
1258(defvar landmark-initial-w0 0.0)
1259(defvar landmark-initial-wij 0.0)
1260(defcustom landmark-no-payoff 0
a7b88742 1261 "The amount of simulation cycles that have occurred with no movement.
323f7c49
SE
1262Used to move the robot when he is stuck in a rut for some reason."
1263 :type 'integer
4d0143e6
JA
1264 :group 'landmark)
1265(defcustom landmark-max-stall-time 2
a7b88742 1266 "The maximum number of cycles that the robot can remain stuck in a place.
4d0143e6 1267After this limit is reached, landmark-random-move is called to push him out of it."
323f7c49 1268 :type 'integer
4d0143e6 1269 :group 'landmark)
178fc2d3
RS
1270
1271
1272;;;_ + Randomizing functions
4d0143e6
JA
1273;;;_ - landmark-flip-a-coin ()
1274(defun landmark-flip-a-coin ()
178fc2d3
RS
1275 (if (> (random 5000) 2500)
1276 -1
1277 1))
4d0143e6
JA
1278;;;_ : landmark-very-small-random-number ()
1279;(defun landmark-very-small-random-number ()
a7b88742
KH
1280; (/
1281; (* (/ (random 900000) 900000.0) .0001)))
4d0143e6
JA
1282;;;_ : landmark-randomize-weights-for (direction)
1283(defun landmark-randomize-weights-for (direction)
c0df1972 1284 (mapc (lambda (target-direction)
178fc2d3 1285 (put direction
a7b88742 1286 target-direction
4d0143e6
JA
1287 (* (landmark-flip-a-coin) (/ (random 10000) 10000.0))))
1288 landmark-directions))
1289;;;_ : landmark-noise ()
1290(defun landmark-noise ()
1291 (* (- (/ (random 30001) 15000.0) 1) landmark-nvar))
1292
1293;;;_ : landmark-fix-weights-for (direction)
1294(defun landmark-fix-weights-for (direction)
c0df1972 1295 (mapc (lambda (target-direction)
178fc2d3 1296 (put direction
a7b88742 1297 target-direction
4d0143e6
JA
1298 landmark-initial-wij))
1299 landmark-directions))
178fc2d3
RS
1300
1301
1302;;;_ + Plotting functions
4d0143e6
JA
1303;;;_ - landmark-plot-internal (sym)
1304(defun landmark-plot-internal (sym)
1305 (landmark-plot-square (landmark-xy-to-index
178fc2d3
RS
1306 (get sym 'x)
1307 (get sym 'y))
1308 (get sym 'sym)))
4d0143e6
JA
1309;;;_ - landmark-plot-landmarks ()
1310(defun landmark-plot-landmarks ()
1311 (setq landmark-cx (/ landmark-board-width 2))
1312 (setq landmark-cy (/ landmark-board-height 2))
178fc2d3 1313
4d0143e6
JA
1314 (put 'landmark-n 'x landmark-cx)
1315 (put 'landmark-n 'y 1)
1316 (put 'landmark-n 'sym 2)
178fc2d3 1317
4d0143e6
JA
1318 (put 'landmark-tree 'x landmark-cx)
1319 (put 'landmark-tree 'y landmark-cy)
1320 (put 'landmark-tree 'sym 6)
178fc2d3 1321
4d0143e6
JA
1322 (put 'landmark-s 'x landmark-cx)
1323 (put 'landmark-s 'y landmark-board-height)
1324 (put 'landmark-s 'sym 3)
178fc2d3 1325
4d0143e6
JA
1326 (put 'landmark-w 'x 1)
1327 (put 'landmark-w 'y (/ landmark-board-height 2))
1328 (put 'landmark-w 'sym 5)
178fc2d3 1329
4d0143e6
JA
1330 (put 'landmark-e 'x landmark-board-width)
1331 (put 'landmark-e 'y (/ landmark-board-height 2))
1332 (put 'landmark-e 'sym 4)
178fc2d3 1333
4d0143e6 1334 (mapc 'landmark-plot-internal '(landmark-n landmark-s landmark-e landmark-w landmark-tree)))
178fc2d3
RS
1335
1336
1337
1338;;;_ + Distance-calculation functions
1339;;;_ - square (a)
1340(defun square (a)
1341 (* a a))
1342
1343;;;_ - distance (x x0 y y0)
1344(defun distance (x x0 y y0)
1345 (sqrt (+ (square (- x x0)) (square (- y y0)))))
1346
1347;;;_ - calc-distance-of-robot-from (direction)
1348(defun calc-distance-of-robot-from (direction)
1349 (put direction 'distance
1350 (distance (get direction 'x)
4d0143e6 1351 (landmark-index-to-x (landmark-point-square))
178fc2d3 1352 (get direction 'y)
4d0143e6 1353 (landmark-index-to-y (landmark-point-square)))))
178fc2d3
RS
1354
1355;;;_ - calc-smell-internal (sym)
1356(defun calc-smell-internal (sym)
1357 (let ((r (get sym 'r))
1358 (d (calc-distance-of-robot-from sym)))
1359 (if (> (* 0.5 (- 1 (/ d r))) 0)
1360 (* 0.5 (- 1 (/ d r)))
1361 0)))
1362
1363
178fc2d3 1364;;;_ + Learning (neural) functions
4d0143e6 1365(defun landmark-f (x)
a7b88742 1366 (cond
4d0143e6 1367 ((> x landmark-bound) landmark-bound)
178fc2d3
RS
1368 ((< x 0.0) 0.0)
1369 (t x)))
1370
4d0143e6 1371(defun landmark-y (direction)
121656e9
JB
1372 (put direction 'noise (landmark-noise))
1373 (put direction 'y_t
1374 (if (> (get direction 's) 0.0)
1375 1.0
1376 0.0)))
178fc2d3 1377
4d0143e6 1378(defun landmark-update-normal-weights (direction)
c0df1972 1379 (mapc (lambda (target-direction)
178fc2d3
RS
1380 (put direction target-direction
1381 (+
1382 (get direction target-direction)
4d0143e6 1383 (* landmark-c
a7b88742 1384 (- (get 'z 't) (get 'z 't-1))
178fc2d3
RS
1385 (get target-direction 'y_t)
1386 (get direction 'smell)))))
4d0143e6 1387 landmark-directions))
a7b88742 1388
4d0143e6 1389(defun landmark-update-naught-weights (direction)
121656e9 1390 (mapc (lambda (_target-direction)
178fc2d3 1391 (put direction 'w0
4d0143e6 1392 (landmark-f
178fc2d3
RS
1393 (+
1394 (get direction 'w0)
4d0143e6 1395 (* landmark-c-naught
a7b88742 1396 (- (get 'z 't) (get 'z 't-1))
178fc2d3 1397 (get direction 'y_t))))))
4d0143e6 1398 landmark-directions))
178fc2d3
RS
1399
1400
178fc2d3
RS
1401;;;_ + Statistics gathering and creating functions
1402
4d0143e6 1403(defun landmark-calc-current-smells ()
c0df1972 1404 (mapc (lambda (direction)
178fc2d3 1405 (put direction 'smell (calc-smell-internal direction)))
4d0143e6 1406 landmark-directions))
178fc2d3 1407
4d0143e6 1408(defun landmark-calc-payoff ()
178fc2d3 1409 (put 'z 't-1 (get 'z 't))
4d0143e6 1410 (put 'z 't (calc-smell-internal 'landmark-tree))
178fc2d3 1411 (if (= (- (get 'z 't) (get 'z 't-1)) 0.0)
a464a6c7 1412 (cl-incf landmark-no-payoff)
4d0143e6 1413 (setf landmark-no-payoff 0)))
178fc2d3 1414
4d0143e6 1415(defun landmark-store-old-y_t ()
c0df1972 1416 (mapc (lambda (direction)
178fc2d3 1417 (put direction 'y_t-1 (get direction 'y_t)))
4d0143e6 1418 landmark-directions))
178fc2d3
RS
1419
1420
a7b88742 1421;;;_ + Functions to move robot
178fc2d3 1422
4d0143e6 1423(defun landmark-confidence-for (target-direction)
c0df1972
SM
1424 (apply '+
1425 (get target-direction 'w0)
1426 (mapcar (lambda (direction)
1427 (*
1428 (get direction target-direction)
1429 (get direction 'smell)))
4d0143e6 1430 landmark-directions)))
178fc2d3
RS
1431
1432
4d0143e6 1433(defun landmark-calc-confidences ()
c0df1972 1434 (mapc (lambda (direction)
4d0143e6
JA
1435 (put direction 's (landmark-confidence-for direction)))
1436 landmark-directions))
178fc2d3 1437
4d0143e6
JA
1438(defun landmark-move ()
1439 (if (and (= (get 'landmark-n 'y_t) 1.0) (= (get 'landmark-s 'y_t) 1.0))
178fc2d3 1440 (progn
4d0143e6
JA
1441 (mapc (lambda (dir) (put dir 'y_t 0)) landmark-ns)
1442 (if landmark-debug
a7b88742 1443 (message "n-s normalization."))))
4d0143e6 1444 (if (and (= (get 'landmark-w 'y_t) 1.0) (= (get 'landmark-e 'y_t) 1.0))
178fc2d3 1445 (progn
4d0143e6
JA
1446 (mapc (lambda (dir) (put dir 'y_t 0)) landmark-ew)
1447 (if landmark-debug
a7b88742 1448 (message "e-w normalization"))))
178fc2d3 1449
c0df1972 1450 (mapc (lambda (pair)
178fc2d3
RS
1451 (if (> (get (car pair) 'y_t) 0)
1452 (funcall (car (cdr pair)))))
1453 '(
4d0143e6
JA
1454 (landmark-n landmark-move-up)
1455 (landmark-s landmark-move-down)
1456 (landmark-e forward-char)
1457 (landmark-w backward-char)))
1458 (landmark-plot-square (landmark-point-square) 1)
a464a6c7 1459 (cl-incf landmark-number-of-moves)
4d0143e6
JA
1460 (if landmark-output-moves
1461 (message "Moves made: %d" landmark-number-of-moves)))
178fc2d3
RS
1462
1463
4d0143e6 1464(defun landmark-random-move ()
a7b88742 1465 (mapc
c0df1972 1466 (lambda (direction) (put direction 'y_t 0))
4d0143e6
JA
1467 landmark-directions)
1468 (dolist (direction (nth (random 8) landmark-8-directions))
178fc2d3 1469 (put direction 'y_t 1.0))
4d0143e6 1470 (landmark-move))
178fc2d3 1471
4d0143e6 1472(defun landmark-amble-robot ()
178fc2d3 1473 (interactive)
4d0143e6 1474 (while (> (calc-distance-of-robot-from 'landmark-tree) 0)
178fc2d3 1475
4d0143e6
JA
1476 (landmark-store-old-y_t)
1477 (landmark-calc-current-smells)
178fc2d3 1478
4d0143e6
JA
1479 (if (> landmark-no-payoff landmark-max-stall-time)
1480 (landmark-random-move)
178fc2d3 1481 (progn
4d0143e6
JA
1482 (landmark-calc-confidences)
1483 (mapc 'landmark-y landmark-directions)
1484 (landmark-move)))
178fc2d3 1485
4d0143e6 1486 (landmark-calc-payoff)
178fc2d3 1487
4d0143e6
JA
1488 (mapc 'landmark-update-normal-weights landmark-directions)
1489 (mapc 'landmark-update-naught-weights landmark-directions)
1490 (if landmark-debug
1491 (landmark-weights-debug)))
1492 (landmark-terminate-game nil))
a7b88742 1493
178fc2d3 1494
4d0143e6
JA
1495;;;_ - landmark-start-robot ()
1496(defun landmark-start-robot ()
1497 "Signal to the Landmark program that you have played.
178fc2d3
RS
1498You must have put the cursor on the square where you want to play.
1499If the game is finished, this command requests for another game."
1500 (interactive)
4d0143e6 1501 (landmark-switch-to-window)
178fc2d3 1502 (cond
4d0143e6
JA
1503 (landmark-emacs-is-computing
1504 (landmark-crash-game))
1505 ((not landmark-game-in-progress)
1506 (landmark-prompt-for-other-game))
178fc2d3 1507 (t
121656e9 1508 (let (square)
4d0143e6 1509 (setq square (landmark-point-square))
178fc2d3 1510 (cond ((null square)
ce5a3ac0 1511 (error "Your point is not on a square. Retry!"))
4d0143e6 1512 ((not (zerop (aref landmark-board square)))
ce5a3ac0 1513 (error "Your point is not on a free square. Retry!"))
178fc2d3
RS
1514 (t
1515 (progn
4d0143e6 1516 (landmark-plot-square square 1)
178fc2d3 1517
4d0143e6
JA
1518 (landmark-store-old-y_t)
1519 (landmark-calc-current-smells)
1520 (put 'z 't (calc-smell-internal 'landmark-tree))
178fc2d3 1521
4d0143e6 1522 (landmark-random-move)
178fc2d3 1523
4d0143e6 1524 (landmark-calc-payoff)
178fc2d3 1525
4d0143e6
JA
1526 (mapc 'landmark-update-normal-weights landmark-directions)
1527 (mapc 'landmark-update-naught-weights landmark-directions)
1528 (landmark-amble-robot)
178fc2d3
RS
1529 )))))))
1530
1531
178fc2d3 1532;;;_ + Misc functions
4d0143e6
JA
1533;;;_ - landmark-init (auto-start save-weights)
1534(defvar landmark-tree-r "")
a7b88742 1535
4d0143e6 1536(defun landmark-init (auto-start save-weights)
178fc2d3 1537
4d0143e6 1538 (setq landmark-number-of-moves 0)
178fc2d3 1539
4d0143e6 1540 (landmark-plot-landmarks)
178fc2d3 1541
4d0143e6 1542 (if landmark-debug
937e6a56 1543 (save-current-buffer
4d0143e6 1544 (set-buffer (get-buffer-create "*landmark-w0*"))
937e6a56 1545 (erase-buffer)
4d0143e6
JA
1546 (set-buffer (get-buffer-create "*landmark-moves*"))
1547 (set-buffer (get-buffer-create "*landmark-wts*"))
937e6a56 1548 (erase-buffer)
4d0143e6 1549 (set-buffer (get-buffer-create "*landmark-y,s,noise*"))
937e6a56 1550 (erase-buffer)
4d0143e6 1551 (set-buffer (get-buffer-create "*landmark-smell*"))
937e6a56 1552 (erase-buffer)
4d0143e6 1553 (set-buffer (get-buffer-create "*landmark-blackbox*"))
937e6a56 1554 (erase-buffer)
4d0143e6 1555 (set-buffer (get-buffer-create "*landmark-distance*"))
937e6a56 1556 (erase-buffer)))
178fc2d3
RS
1557
1558
4d0143e6 1559 (landmark-set-landmark-signal-strengths)
178fc2d3 1560
4d0143e6 1561 (dolist (direction landmark-directions)
937e6a56 1562 (put direction 'y_t 0.0))
178fc2d3
RS
1563
1564 (if (not save-weights)
1565 (progn
4d0143e6
JA
1566 (mapc 'landmark-fix-weights-for landmark-directions)
1567 (dolist (direction landmark-directions)
1568 (put direction 'w0 landmark-initial-w0)))
178fc2d3
RS
1569 (message "Weights preserved for this run."))
1570
1571 (if auto-start
1572 (progn
4d0143e6
JA
1573 (landmark-goto-xy (1+ (random landmark-board-width)) (1+ (random landmark-board-height)))
1574 (landmark-start-robot))))
a7b88742 1575
178fc2d3
RS
1576
1577;;;_ - something which doesn't work
1578; no-a-worka!!
4c36be58 1579;(defun landmark-sum-list (list)
178fc2d3 1580; (if (> (length list) 0)
4d0143e6 1581; (+ (car list) (landmark-sum-list (cdr list)))
178fc2d3
RS
1582; 0))
1583; this a worka!
1584; (eval (cons '+ list))
4d0143e6 1585;;;_ - landmark-set-landmark-signal-strengths ()
a464a6c7
SM
1586;; on a screen higher than wide, I noticed that the robot would amble
1587;; left and right and not move forward. examining *landmark-blackbox*
1588;; revealed that there was no scent from the north and south
1589;; landmarks, hence, they need less factoring down of the effect of
1590;; distance on scent.
178fc2d3 1591
4d0143e6
JA
1592(defun landmark-set-landmark-signal-strengths ()
1593 (setq landmark-tree-r (* (sqrt (+ (square landmark-cx) (square landmark-cy))) 1.5))
c0df1972 1594 (mapc (lambda (direction)
4d0143e6
JA
1595 (put direction 'r (* landmark-cx 1.1)))
1596 landmark-ew)
c0df1972 1597 (mapc (lambda (direction)
4d0143e6
JA
1598 (put direction 'r (* landmark-cy 1.1)))
1599 landmark-ns)
1600 (put 'landmark-tree 'r landmark-tree-r))
178fc2d3
RS
1601
1602
4d0143e6 1603;;;_ + landmark-test-run ()
178fc2d3 1604
998c789c 1605;;;###autoload
4d0143e6 1606(defalias 'landmark-repeat 'landmark-test-run)
998c789c 1607;;;###autoload
4d0143e6
JA
1608(defun landmark-test-run ()
1609 "Run 100 Landmark games, each time saving the weights from the previous game."
178fc2d3 1610 (interactive)
4d0143e6 1611 (landmark 1)
178fc2d3 1612 (dotimes (scratch-var 100)
4d0143e6 1613 (landmark 2)))
178fc2d3 1614
998c789c 1615;;;###autoload
4d0143e6
JA
1616(defun landmark (parg)
1617 "Start or resume an Landmark game.
178fc2d3
RS
1618If a game is in progress, this command allows you to resume it.
1619Here is the relation between prefix args and game options:
1620
1621prefix arg | robot is auto-started | weights are saved from last game
1622---------------------------------------------------------------------
1623none / 1 | yes | no
1624 2 | yes | yes
1625 3 | no | yes
1626 4 | no | no
1627
4d0143e6 1628You start by moving to a square and typing \\[landmark-start-robot],
998c789c 1629if you did not use a prefix arg to ask for automatic start.
178fc2d3 1630Use \\[describe-mode] for more info."
65569e52 1631 (interactive "p")
178fc2d3 1632
4d0143e6
JA
1633 (setf landmark-n nil landmark-m nil)
1634 (landmark-switch-to-window)
178fc2d3 1635 (cond
4d0143e6
JA
1636 (landmark-emacs-is-computing
1637 (landmark-crash-game))
1638 ((or (not landmark-game-in-progress)
1639 (<= landmark-number-of-moves 2))
1640 (let ((max-width (landmark-max-width))
1641 (max-height (landmark-max-height)))
1642 (or landmark-n (setq landmark-n max-width))
1643 (or landmark-m (setq landmark-m max-height))
1644 (cond ((< landmark-n 1)
178fc2d3 1645 (error "I need at least 1 column"))
4d0143e6 1646 ((< landmark-m 1)
178fc2d3 1647 (error "I need at least 1 row"))
4d0143e6
JA
1648 ((> landmark-n max-width)
1649 (error "I cannot display %d columns in that window" landmark-n)))
1650 (if (and (> landmark-m max-height)
1651 (not (eq landmark-m landmark-saved-board-height))
178fc2d3 1652 ;; Use EQ because SAVED-BOARD-HEIGHT may be nil
4d0143e6
JA
1653 (not (y-or-n-p (format "Do you really want %d rows? " landmark-m))))
1654 (setq landmark-m max-height)))
1655 (if landmark-one-moment-please
a7b88742 1656 (message "One moment, please..."))
4d0143e6
JA
1657 (landmark-start-game landmark-n landmark-m)
1658 (eval (cons 'landmark-init
a7b88742
KH
1659 (cond
1660 ((= parg 1) '(t nil))
1661 ((= parg 2) '(t t))
1662 ((= parg 3) '(nil t))
1663 ((= parg 4) '(nil nil))
1664 (t '(nil t))))))))
1665
1666
178fc2d3
RS
1667;;;_ + Local variables
1668
5a6c1d87 1669;;; The following `allout-layout' local variable setting:
178fc2d3
RS
1670;;; - closes all topics from the first topic to just before the third-to-last,
1671;;; - shows the children of the third to last (config vars)
1672;;; - and the second to last (code section),
1673;;; - and closes the last topic (this local-variables section).
1674;;;Local variables:
5a6c1d87 1675;;;allout-layout: (0 : -1 -1 0)
178fc2d3
RS
1676;;;End:
1677
7130b08f
KH
1678(provide 'landmark)
1679
178fc2d3 1680;;; landmark.el ends here