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