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