ducpel.el 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276
  1. ;;; ducpel.el --- Logic game with sokoban elements
  2. ;; Copyright (C) 2014 Alex Kost
  3. ;; Author: Alex Kost <alezost@gmail.com>
  4. ;; Created: 31 Mar 2014
  5. ;; Version: 0.1
  6. ;; Package-Requires: ((cl-lib "0.5"))
  7. ;; URL: https://github.com/alezost/ducpel
  8. ;; Keywords: games
  9. ;; This program is free software; you can redistribute it and/or modify
  10. ;; it under the terms of the GNU General Public License as published by
  11. ;; the Free Software Foundation, either version 3 of the License, or
  12. ;; (at your option) any later version.
  13. ;; This program is distributed in the hope that it will be useful,
  14. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;; GNU General Public License for more details.
  17. ;; You should have received a copy of the GNU General Public License
  18. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. ;;; Commentary:
  20. ;; To install the game manually, you need:
  21. ;;
  22. ;; - "ducpel.el" (this file);
  23. ;; - "ducpel-glyphs.el" (it generates default images);
  24. ;; - directory with levels.
  25. ;;
  26. ;; Add the following to your emacs init file:
  27. ;;
  28. ;; (add-to-list 'load-path "/path/to/ducpel-dir")
  29. ;; (autoload 'ducpel "ducpel" nil t)
  30. ;;
  31. ;; Also if you keep levels separately:
  32. ;;
  33. ;; (setq ducpel-levels-directory "/path/to/ducpel-levels-dir")
  34. ;; After that you can "M-x ducpel" and enjoy. Use:
  35. ;;
  36. ;; - arrow keys to move your man;
  37. ;; - TAB to switch to another man;
  38. ;; - "u" to undo a move;
  39. ;; - SPC to activate a special cell (exit or teleport);
  40. ;; - "R" to restart the level;
  41. ;; - "N"/"P"/"L" to go to the next/previous/particular level.
  42. ;; At any time you can replay your moves by pressing "rc" (2 keys). If
  43. ;; you feel that a level is impassable, you may surrender (and see a
  44. ;; solution) by pressing "rS".
  45. ;; Contact the maintainer please, if you found a better solution (with
  46. ;; less moves) for some level or if you made an interesting level that
  47. ;; can become a part of ducpel.
  48. ;; For full documentation, see <https://github.com/alezost/ducpel>.
  49. ;;; Code:
  50. (require 'cl-lib)
  51. (require 'gamegrid)
  52. ;;; User options
  53. (defgroup ducpel nil
  54. "Logic game."
  55. :group 'games)
  56. (defcustom ducpel-use-glyphs t
  57. "If non-nil, use glyphs when available."
  58. :type 'boolean
  59. :group 'ducpel)
  60. (defcustom ducpel-buffer-name "*ducpel*"
  61. "Name of the ducpel buffer."
  62. :type 'string
  63. :group 'ducpel)
  64. (defcustom ducpel-replay-pause 0.5
  65. "Number of seconds to wait between moves during replaying.
  66. To replay the moves, use
  67. \\[ducpel-replay-current] in a ducpel buffer."
  68. :type 'number
  69. :group 'ducpel)
  70. (defcustom ducpel-levels-directory
  71. (expand-file-name "levels"
  72. (file-name-directory (locate-library "ducpel")))
  73. "Directory with level files."
  74. :type 'directory
  75. :group 'ducpel)
  76. (defcustom ducpel-user-levels-directory user-emacs-directory
  77. "Directory with additional level files.
  78. To load a level from this directory, use
  79. \\[ducpel-load-level-from-file] in a ducpel buffer."
  80. :type 'directory
  81. :group 'ducpel)
  82. (defcustom ducpel-user-saves-directory user-emacs-directory
  83. "Directory with saves of moves.
  84. To replay saved moves from this directory, use
  85. \\[ducpel-replay-from-file] in a ducpel buffer."
  86. :type 'directory
  87. :group 'ducpel)
  88. (defcustom ducpel-default-level 1
  89. "Default level."
  90. :type 'integer
  91. :group 'ducpel)
  92. ;;; Constants
  93. ;; Cell types
  94. (defconst ducpel-empty 0)
  95. (defconst ducpel-wall 1)
  96. (defconst ducpel-impassable 2)
  97. (defconst ducpel-floor 3)
  98. (defconst ducpel-man 4)
  99. (defconst ducpel-active-man 5)
  100. (defconst ducpel-box 6)
  101. ;; Floor/box types
  102. (defconst ducpel-simple 0)
  103. (defconst ducpel-exit 1)
  104. (defconst ducpel-teleport 2)
  105. (defconst ducpel-left 3)
  106. (defconst ducpel-right 4)
  107. (defconst ducpel-up 5)
  108. (defconst ducpel-down 6)
  109. (defconst ducpel-horizontal 7)
  110. (defconst ducpel-vertical 8)
  111. (defconst ducpel-multi 9)
  112. (defconst ducpel-cell-types
  113. (list ducpel-empty ducpel-wall ducpel-impassable
  114. ducpel-floor ducpel-man ducpel-active-man ducpel-box)
  115. "List of available cell types.")
  116. (defconst ducpel-floor-types
  117. (list ducpel-simple ducpel-exit ducpel-teleport
  118. ducpel-left ducpel-right ducpel-up ducpel-down
  119. ducpel-horizontal ducpel-vertical ducpel-multi)
  120. "List of available floor/box types.")
  121. ;; The maximum count of cell characters is limited to 256.
  122. ;; Currently we have:
  123. ;;
  124. ;; - by 1 for empty, wall and impassable cells;
  125. ;; - 10 floors;
  126. ;; - 20 men (active and passive on each floor);
  127. ;; - 100 boxes (each box type on each floor).
  128. ;;
  129. ;; So there is a space to avoid printable ASCII characters and some
  130. ;; control characters (see (info "(elisp) Usual Display")) to be able to
  131. ;; write something in a ducpel buffer along with having the map of
  132. ;; glyphs. If the count of cell types is increased significantly, we
  133. ;; will have to use printable chars and thus to refuse writing text in
  134. ;; the buffer (and perhaps to use the modeline instead).
  135. (defconst ducpel-init-cell-char-alist
  136. (let ((len (length ducpel-floor-types))
  137. (floor-shift 126))
  138. (list
  139. (cons ducpel-empty 23)
  140. (cons ducpel-wall 24)
  141. (cons ducpel-impassable 25)
  142. (cons ducpel-floor floor-shift)
  143. (cons ducpel-man (+ len floor-shift))
  144. (cons ducpel-active-man (+ (* 2 len) floor-shift))
  145. (cons ducpel-box (+ (* 3 len) floor-shift))))
  146. "Alist of initial cell characters for the cell types.
  147. Car of each assoc is a cell type. Cdr is a cell character.")
  148. ;; Move types
  149. (defconst ducpel-left-move #b0001)
  150. (defconst ducpel-right-move #b0010)
  151. (defconst ducpel-up-move #b0100)
  152. (defconst ducpel-down-move #b1000)
  153. (defconst ducpel-action 3)
  154. (defconst ducpel-move-type-alist
  155. (list
  156. (cons ducpel-left ducpel-left-move)
  157. (cons ducpel-right ducpel-right-move)
  158. (cons ducpel-up ducpel-up-move)
  159. (cons ducpel-down ducpel-down-move)
  160. (cons ducpel-horizontal (+ ducpel-left-move ducpel-right-move))
  161. (cons ducpel-vertical (+ ducpel-up-move ducpel-down-move))
  162. (cons ducpel-multi (+ ducpel-left-move ducpel-right-move
  163. ducpel-up-move ducpel-down-move)))
  164. "Alist of possible moves for the floor types.
  165. Car of each assoc is a floor type. Cdr is a move type.")
  166. (defconst ducpel-break-wall-power 3
  167. "Power (minimum count of pushing men) required to break a wall.")
  168. ;; Constants for parsing level maps
  169. (defconst ducpel-map-re "^;+ *Map")
  170. (defconst ducpel-objects-re "^;+ *Objects")
  171. (defconst ducpel-solution-re "^;+ *Solution")
  172. (defconst ducpel-ignored-line-re
  173. (rx line-start
  174. (or (and ";" (* any))
  175. (* blank))
  176. line-end)
  177. "Regexp for ignored lines in level files.")
  178. (defconst ducpel-empty-map-char ?\s)
  179. (defconst ducpel-wall-map-char ?#)
  180. (defconst ducpel-impassable-map-char ?@)
  181. (defconst ducpel-floor-map-chars ".ETLRUDHVM")
  182. (defconst ducpel-box-map-chars "betlrudhvm")
  183. (defconst ducpel-man-map-char ?p)
  184. (defconst ducpel-active-man-map-char ?P)
  185. ;;; General variables
  186. (defvar ducpel-men []
  187. "Array of coordinates of the men on the current level.
  188. Each element of the list is a list of the form (X Y).")
  189. (defvar ducpel-active-man-index 0
  190. "Index of the active man in `ducpel-men'.")
  191. (defvar ducpel-teleports nil
  192. "List of coordinates of the teleports on the current level.
  193. Each element of the list is a list of the form (X Y).")
  194. (defvar ducpel-undo-list nil
  195. "List of full undo information.
  196. Each element of the list has a form:
  197. (CELLS MEN ACTIVE TELEPORTS)
  198. CELLS has a form of `ducpel-undo-current-cells'.
  199. MEN has a form of `ducpel-undo-current-men'.
  200. ACTIVE has a form of `ducpel-undo-current-active-index'.
  201. TELEPORTS has a form of `ducpel-undo-current-teleports'.")
  202. (defvar ducpel-undo-current-cells nil
  203. "List of changes of the cells made after the last move.
  204. Each element of the list has a form:
  205. (X Y CHAR)
  206. X, Y - coordinates of the changed cell;
  207. CHAR is a gamegrid character of the changed cell.
  208. If nil, it means the cells were not changed.")
  209. (defvar ducpel-undo-current-men []
  210. "Array of men coordinates changed after the last move.
  211. Has a form of `ducpel-men'. If an element of the array is nil,
  212. it means the coordinates of the man were not changed.")
  213. (defvar ducpel-undo-current-active-index nil
  214. "Index of the man that was active after the last move.
  215. If nil, it means the active man was not changed.")
  216. (defvar ducpel-undo-current-teleports nil
  217. "List of coordinates of the teleports after the last move.
  218. Has a form of `ducpel-teleports'.
  219. If nil, it means teleports were not changed.")
  220. (defvar ducpel-moves 0
  221. "The number of moves for the current level.")
  222. (defvar ducpel-done 0
  223. "The number of men went to a better world.")
  224. (defvar ducpel-moves-history nil
  225. "List of moves for the current level.
  226. Each element of the list has a form:
  227. (MAN MOVE-TYPE)
  228. MAN is the index (from `ducpel-men') of a man who made the move.
  229. For the meaning of MOVE-TYPE, see `ducpel-do'.
  230. Car of the list is the last move; the last element of the list is
  231. the first move.")
  232. (defvar ducpel-solution nil
  233. "List of moves to solve the current level.
  234. Has a form of `ducpel-moves-history'.")
  235. (defvar ducpel-level-data nil
  236. "Data of the current level map.
  237. 2-dimensional matrix (vector of vectors) of the width
  238. `ducpel-width' and the height `ducpel-height' that contains cell
  239. characters for the current level.")
  240. (defvar ducpel-level nil
  241. "Index of the current level.")
  242. (defvar ducpel-level-file nil
  243. "Name of file with a map of the current level.")
  244. (defvar ducpel-width 0
  245. "Width of the current level map.")
  246. (defvar ducpel-height 0
  247. "Height of the current level map.")
  248. ;;; Cells
  249. (defvar ducpel-cell-plists (make-vector 256 nil)
  250. "Array of property lists for all possible cell characters.
  251. Properties in property lists:
  252. `:type' - type of the cell - element from `ducpel-cell-types';
  253. `:floor'/`:box' (optional) - type of the floor/box - element from
  254. `ducpel-floor-types'.")
  255. (defun ducpel-get-cell-char-by-plist (&rest plist)
  256. "Return cell character by the property list PLIST."
  257. (let ((type (plist-get plist :type))
  258. (floor-index (or (plist-get plist :floor) 0))
  259. (box-index (or (plist-get plist :box) 0))
  260. (len (length ducpel-floor-types)))
  261. (let ((init-char (cdr (assoc type ducpel-init-cell-char-alist))))
  262. (+ init-char
  263. floor-index
  264. (* box-index len)))))
  265. (defun ducpel-init-cell-plists ()
  266. "Fill `ducpel-cell-plists'."
  267. (cl-flet ((pset (&rest plist)
  268. (aset ducpel-cell-plists
  269. (apply 'ducpel-get-cell-char-by-plist plist)
  270. plist)))
  271. (pset :type ducpel-empty)
  272. (pset :type ducpel-wall)
  273. (pset :type ducpel-impassable)
  274. (dolist (floor ducpel-floor-types)
  275. (pset :type ducpel-floor :floor floor)
  276. (pset :type ducpel-man :floor floor)
  277. (pset :type ducpel-active-man :floor floor)
  278. (dolist (box ducpel-floor-types)
  279. (pset :type ducpel-box :floor floor :box box)))))
  280. (ducpel-init-cell-plists)
  281. (defun ducpel-get-cell-plist-by-cell-char (char)
  282. "Return cell property list by the cell character CHAR."
  283. (aref ducpel-cell-plists char))
  284. (defun ducpel-get-cell-plist-by-xy (x y)
  285. "Return cell property list by the cell coordinates X, Y."
  286. (ducpel-get-cell-plist-by-cell-char
  287. (gamegrid-get-cell x y)))
  288. (defun ducpel-set-cell (x y &rest plist)
  289. "Set cell at X, Y to the cell defined by property list PLIST.
  290. Return cell character of the set cell."
  291. (let* ((old-char (gamegrid-get-cell x y))
  292. (new-char (apply 'ducpel-get-cell-char-by-plist plist)))
  293. (gamegrid-set-cell x y new-char)
  294. (push (list x y old-char) ducpel-undo-current-cells)
  295. new-char))
  296. ;;; Men
  297. (defun ducpel-get-man-index-by-shift (shift &optional index)
  298. "Return new index by shifting man INDEX with SHIFT.
  299. If INDEX is nil, use `ducpel-active-man-index'."
  300. (ducpel-get-index-by-shift
  301. (length ducpel-men)
  302. (or index ducpel-active-man-index)
  303. shift))
  304. (defun ducpel-get-man-xy (&optional index)
  305. "Return coordinates of a man.
  306. INDEX is a number of the man in `ducpel-men'. If INDEX is nil,
  307. use `ducpel-active-man-index'.
  308. Returning value is a list of the form (X Y)."
  309. (or index
  310. (setq index ducpel-active-man-index))
  311. (aref ducpel-men index))
  312. (defun ducpel-get-man-index-by-xy (x y)
  313. "Return index of a man placed on X, Y cell."
  314. (or (ducpel-get-index-by-element
  315. ducpel-men (list x y) 'equal 'noerror)
  316. (error "No man with %d, %d coordinates"
  317. x y)))
  318. (defun ducpel-set-man-xy (from-x from-y to-x to-y)
  319. "Set coordinates of a man from FROM-X, FROM-Y to TO-X, TO-Y."
  320. (let ((index (ducpel-get-man-index-by-xy from-x from-y)))
  321. (aset ducpel-undo-current-men index (list from-x from-y))
  322. (aset ducpel-men index (list to-x to-y))))
  323. (defun ducpel-delete-man (index)
  324. "Delete man INDEX from the current map."
  325. (cl-multiple-value-bind (x y)
  326. (ducpel-get-man-xy index)
  327. (aset ducpel-undo-current-men index (list x y))
  328. (aset ducpel-men index nil)
  329. (let ((plist (ducpel-get-cell-plist-by-xy x y)))
  330. (ducpel-set-cell x y
  331. :type ducpel-floor
  332. :floor (plist-get plist :floor)))))
  333. (defun ducpel-set-active-man (index)
  334. "Try to set a man INDEX active.
  335. INDEX is a number of the man in `ducpel-men'. If the man does
  336. not exist, try to set the next man active, and so on.
  337. Return index of the new active man or nil if no man was set."
  338. (unless (and (= index ducpel-active-man-index)
  339. (aref ducpel-men index))
  340. (ducpel-set-active-man-1
  341. index (ducpel-get-man-index-by-shift -1 index))))
  342. (defun ducpel-set-active-man-1 (index exit-index)
  343. "Set a man active.
  344. INDEX is a number of the man in `ducpel-men'. If the man does
  345. not exist, try to set the next man active, and so on until the
  346. man with index EXIT-INDEX will not be achieved. In this case,
  347. return nil; otherwise return index of the new active man."
  348. (unless (= index exit-index)
  349. (cl-multiple-value-bind (new-x new-y)
  350. (ducpel-get-man-xy index)
  351. (if (null new-x)
  352. (ducpel-set-active-man-1
  353. (ducpel-get-man-index-by-shift 1 index)
  354. exit-index)
  355. (cl-multiple-value-bind (old-x old-y)
  356. (ducpel-get-man-xy)
  357. (when old-x ; previously active man could be "Done" already
  358. (let ((old-plist (ducpel-get-cell-plist-by-xy old-x old-y)))
  359. (ducpel-set-cell old-x old-y
  360. :type ducpel-man
  361. :floor (plist-get old-plist :floor)))))
  362. (let ((new-plist (ducpel-get-cell-plist-by-xy new-x new-y)))
  363. (ducpel-set-cell new-x new-y
  364. :type ducpel-active-man
  365. :floor (plist-get new-plist :floor)))
  366. (or ducpel-undo-current-active-index
  367. (setq ducpel-undo-current-active-index
  368. ducpel-active-man-index))
  369. (setq ducpel-active-man-index index)))))
  370. (defun ducpel-get-active-cell-xy ()
  371. "Return coordinates of the cell with the active man.
  372. Returning value is a list of the form (X Y)."
  373. (aref ducpel-men ducpel-active-man-index))
  374. (defun ducpel-get-active-cell-plist ()
  375. "Return cell plist of the cell with the active man."
  376. (apply 'ducpel-get-cell-plist-by-xy
  377. (ducpel-get-active-cell-xy)))
  378. (defun ducpel-next-man ()
  379. "Select next man."
  380. (interactive)
  381. (ducpel-set-active-man (ducpel-get-man-index-by-shift 1)))
  382. (defun ducpel-previous-man ()
  383. "Select previous man."
  384. (interactive)
  385. (ducpel-set-active-man (ducpel-get-man-index-by-shift -1)))
  386. ;;; Doing (moves and actions)
  387. (defun ducpel-do (move-type)
  388. "Try to make a move or perform an action with active man.
  389. Save undo history if the move/action was successful.
  390. MOVE-TYPE is one of the following constants: `ducpel-action',
  391. `ducpel-left-move', `ducpel-right-move', `ducpel-up-move',
  392. `ducpel-down-move'."
  393. (unless (ducpel-done-p t)
  394. (let ((man ducpel-active-man-index))
  395. (when (if (eql move-type ducpel-action)
  396. (ducpel-do-action)
  397. (ducpel-do-move move-type))
  398. (ducpel-add-move)
  399. (push (list man move-type) ducpel-moves-history)
  400. (ducpel-undo-save-current)))))
  401. (defun ducpel-do-action ()
  402. "Perform an action on the current cell.
  403. Return non-nil if the action was successful."
  404. (let* ((plist (ducpel-get-active-cell-plist))
  405. (floor (plist-get plist :floor))
  406. success)
  407. (cond
  408. ((eql floor ducpel-exit)
  409. (ducpel-delete-man ducpel-active-man-index)
  410. (ducpel-set-active-man (ducpel-get-man-index-by-shift 1))
  411. (ducpel-check-done)
  412. (ducpel-print-done)
  413. (ducpel-done-p t)
  414. (setq success t))
  415. ((eql floor ducpel-teleport)
  416. (if (null (cdr ducpel-teleports))
  417. ;; If a single teleport on the map
  418. (message "This strange thing looks broken.")
  419. (if (ducpel-teleport-active-man)
  420. (setq success t)
  421. (message "Hm, perhaps the teleport is blocked."))))
  422. (t (message "Nothing interesting here.")))
  423. success))
  424. (defun ducpel-do-move (direction)
  425. "Move active man in the DIRECTION.
  426. For the meaning of DIRECTION, see `ducpel-cell-can-move-p'.
  427. Return non-nil if the move was successful."
  428. (cl-multiple-value-bind (x y)
  429. (ducpel-get-man-xy)
  430. (ducpel-move x y direction)))
  431. (defun ducpel-teleport-active-man ()
  432. "Try to teleport active man to a free teleport cell.
  433. If the next teleport after the current one is blocked, try the
  434. next after it and so on.
  435. Return non-nil, if teleportation was successful."
  436. (let* ((active-xy (ducpel-get-active-cell-xy))
  437. (next-teleports (member active-xy ducpel-teleports)))
  438. (or next-teleports
  439. (error "Active man is not on the teleport cell"))
  440. ;; Getting next free teleport: if the rest teleports are blocked,
  441. ;; continue searching from the beginning of `ducpel-teleports'.
  442. (let ((xy (or (ducpel-teleport-get-free-cell (cdr next-teleports))
  443. (ducpel-teleport-get-free-cell
  444. (cl-loop for teleport in ducpel-teleports
  445. until (equal teleport active-xy)
  446. collect teleport)))))
  447. (when xy
  448. (let ((from-x (car active-xy))
  449. (from-y (cadr active-xy))
  450. (to-x (car xy))
  451. (to-y (cadr xy)))
  452. (ducpel-set-cell
  453. to-x to-y
  454. :type ducpel-active-man :floor ducpel-teleport)
  455. (ducpel-set-cell
  456. from-x from-y
  457. :type ducpel-floor :floor ducpel-teleport)
  458. (ducpel-set-man-xy from-x from-y to-x to-y)
  459. t)))))
  460. (defun ducpel-teleport-get-free-cell (cells)
  461. "Return first free cell from a list of coordinates CELLS.
  462. Cell is free if it is a floor with no object (man or box) on it.
  463. Return nil if none of the cells is free."
  464. (cl-loop for cell in cells
  465. if (eql (plist-get
  466. (apply 'ducpel-get-cell-plist-by-xy cell)
  467. :type)
  468. ducpel-floor)
  469. return cell))
  470. (defun ducpel-cell-can-move-p (floor-type direction)
  471. "Return non-nil, if a cell with FLOOR-TYPE can move in the DIRECTION.
  472. Direction should have a value of one of the following constants:
  473. `ducpel-left-move', `ducpel-right-move',
  474. `ducpel-up-move', `ducpel-down-move'."
  475. (let ((moves (cdr (assoc floor-type ducpel-move-type-alist))))
  476. (and moves
  477. (/= 0 (logand moves direction)))))
  478. (defun ducpel-get-xy (from-x from-y direction &optional val)
  479. "Return coordinates by shifting FROM-X, FROM-Y to the DIRECTION by VAL.
  480. For the meaning of DIRECTION, see `ducpel-cell-can-move-p'.
  481. If VAL is nil, shift coordinates by 1.
  482. Returning value is a list of the form (X Y)."
  483. (let ((x from-x)
  484. (y from-y)
  485. (val (or val 1)))
  486. (cond
  487. ((eql direction ducpel-left-move) (cl-decf x val))
  488. ((eql direction ducpel-right-move) (cl-incf x val))
  489. ((eql direction ducpel-up-move) (cl-decf y val))
  490. ((eql direction ducpel-down-move) (cl-incf y val)))
  491. (list x y)))
  492. (defun ducpel-get-last-empty-xy (x y direction)
  493. "Return last cell of `ducpel-empty' type by moving from X, Y in DIRECTION.
  494. For the meaning of DIRECTION, see `ducpel-cell-can-move-p'.
  495. Returning value is a list of coordinates of the last empty cell."
  496. (let (next-x next-y)
  497. (while (progn
  498. (cl-multiple-value-setq (next-x next-y)
  499. (ducpel-get-xy x y direction))
  500. (let* ((char (gamegrid-get-cell next-x next-y))
  501. (plist (ducpel-get-cell-plist-by-cell-char char))
  502. (type (plist-get plist :type)))
  503. (eql type ducpel-empty)))
  504. (setq x next-x
  505. y next-y))
  506. (list x y)))
  507. (defun ducpel-check-done ()
  508. "Count and set `ducpel-done'."
  509. (let ((done 0))
  510. (dotimes (i (length ducpel-men))
  511. (or (aref ducpel-men i) (cl-incf done)))
  512. (setq ducpel-done done)))
  513. (defun ducpel-done-p (&optional show-message)
  514. "Return non-nil if current level is passed.
  515. If SHOW-MESSAGE is non-nil, also show a message in minibuffer."
  516. (let ((done (= ducpel-done (length ducpel-men))))
  517. (and done
  518. show-message
  519. ;; FIXME Do not hardcode the bindings
  520. (message "DONE! Press 'r c' to replay, 'r s' to save, 'R' to restart, 'N' for the next level."))
  521. done))
  522. (defun ducpel-add-move ()
  523. "Increase the current count of moves."
  524. (cl-incf ducpel-moves)
  525. (ducpel-print-moves))
  526. (defun ducpel-remove-move ()
  527. "Decrease the current count of moves."
  528. (cl-decf ducpel-moves)
  529. (ducpel-print-moves))
  530. (defun ducpel-action ()
  531. "Perform an action on the current cell."
  532. (interactive)
  533. (ducpel-do ducpel-action))
  534. (defun ducpel-move-left ()
  535. "Move one cell left."
  536. (interactive)
  537. (ducpel-do ducpel-left-move))
  538. (defun ducpel-move-right ()
  539. "Move one cell right."
  540. (interactive)
  541. (ducpel-do ducpel-right-move))
  542. (defun ducpel-move-up ()
  543. "Move one cell up."
  544. (interactive)
  545. (ducpel-do ducpel-up-move))
  546. (defun ducpel-move-down ()
  547. "Move one cell down."
  548. (interactive)
  549. (ducpel-do ducpel-down-move))
  550. ;; The following variables are used only during a move by
  551. ;; `ducpel-move-<smth>-to-<smth>' functions and are set by
  552. ;; `ducpel-move'.
  553. (defvar ducpel-from-x nil)
  554. (defvar ducpel-from-y nil)
  555. (defvar ducpel-from-char nil)
  556. (defvar ducpel-from-plist nil)
  557. (defvar ducpel-from-type nil)
  558. (defvar ducpel-to-x nil)
  559. (defvar ducpel-to-y nil)
  560. (defvar ducpel-to-char nil)
  561. (defvar ducpel-to-plist nil)
  562. (defvar ducpel-to-type nil)
  563. (defvar ducpel-power nil)
  564. (defvar ducpel-direction nil)
  565. (defun ducpel-move (x y direction &optional power)
  566. "Move cell at X, Y in the DIRECTION with POWER.
  567. For the meaning of DIRECTION, see `ducpel-cell-can-move-p'.
  568. Return non-nil if the shift was successful, nil otherwise."
  569. (let* ((ducpel-from-x x)
  570. (ducpel-from-y y)
  571. (ducpel-power (or power 0))
  572. (ducpel-direction direction)
  573. (ducpel-from-char (gamegrid-get-cell x y))
  574. (ducpel-from-plist (ducpel-get-cell-plist-by-cell-char
  575. ducpel-from-char))
  576. (ducpel-from-type (plist-get ducpel-from-plist :type))
  577. success)
  578. ;; Most cell types can't be moved
  579. (unless (memql ducpel-from-type
  580. (list ducpel-empty ducpel-wall
  581. ducpel-impassable ducpel-floor))
  582. (cl-multiple-value-bind (ducpel-to-x ducpel-to-y)
  583. (ducpel-get-xy ducpel-from-x ducpel-from-y
  584. ducpel-direction)
  585. (let* ((ducpel-to-char (gamegrid-get-cell ducpel-to-x ducpel-to-y))
  586. (ducpel-to-plist (ducpel-get-cell-plist-by-cell-char
  587. ducpel-to-char))
  588. (ducpel-to-type (plist-get ducpel-to-plist :type)))
  589. (cond
  590. ;; If a move is successful, redraw only the destination cell
  591. ;; (`ducpel-to-x', `ducpel-to-y'). If it was a move of the
  592. ;; active man, also redraw the departure cell
  593. ;; (`ducpel-from-x', `ducpel-from-y').
  594. ;; We want to move a MAN
  595. ((eql ducpel-from-type ducpel-man)
  596. (cl-incf ducpel-power)
  597. (when (or (ducpel-move-object-to-floor)
  598. (ducpel-move-object-to-wall))
  599. (ducpel-set-man-xy ducpel-from-x ducpel-from-y
  600. ducpel-to-x ducpel-to-y)
  601. (setq success t)))
  602. ;; We want to move an ACTIVE MAN
  603. ((eql ducpel-from-type ducpel-active-man)
  604. (cl-incf ducpel-power)
  605. (let ((new-from-plist
  606. (cond
  607. ((or (ducpel-move-object-to-floor)
  608. (ducpel-move-object-to-wall))
  609. (list :type ducpel-floor
  610. :floor (plist-get ducpel-from-plist :floor)))
  611. ((ducpel-move-man-to-emty)
  612. (list :type ducpel-empty)))))
  613. (when new-from-plist
  614. (ducpel-set-man-xy ducpel-from-x ducpel-from-y
  615. ducpel-to-x ducpel-to-y)
  616. (apply 'ducpel-set-cell
  617. ducpel-from-x ducpel-from-y new-from-plist)
  618. (setq success t))))
  619. ;; We want to move a BOX
  620. ((and (eql ducpel-from-type ducpel-box)
  621. (> ducpel-power 0))
  622. (cl-decf ducpel-power)
  623. (when (or (ducpel-move-object-to-floor)
  624. (ducpel-move-object-to-wall)
  625. (ducpel-move-box-to-empty))
  626. (setq success t)))))))
  627. success))
  628. (defun ducpel-move-object-to-floor ()
  629. "Try to move an object (man or box) to a floor.
  630. If a destination cell contains another object, try to move it at first.
  631. If the move is possible, redraw the destination cell and
  632. return non-nil."
  633. (when (or (eql ducpel-to-type ducpel-floor)
  634. (and (or (eql ducpel-to-type ducpel-man)
  635. (eql ducpel-to-type ducpel-box))
  636. (ducpel-move ducpel-to-x ducpel-to-y
  637. ducpel-direction ducpel-power)))
  638. (ducpel-set-cell ducpel-to-x ducpel-to-y
  639. :type ducpel-from-type
  640. :floor (plist-get ducpel-to-plist :floor)
  641. :box (plist-get ducpel-from-plist :box))))
  642. (defun ducpel-move-object-to-wall ()
  643. "Try to move an object (man or box) to a wall.
  644. If the move is possible, redraw the destination cell and
  645. return non-nil."
  646. (when (and (eql ducpel-to-type ducpel-wall)
  647. (>= ducpel-power ducpel-break-wall-power))
  648. (ducpel-set-cell ducpel-to-x ducpel-to-y
  649. :type ducpel-from-type :floor ducpel-simple)))
  650. (defun ducpel-move-man-to-emty ()
  651. "Try to move a man to an empty cell.
  652. If the move is possible, redraw the destination cell and
  653. return non-nil."
  654. (when (and (eql ducpel-to-type ducpel-empty)
  655. (ducpel-cell-can-move-p
  656. (plist-get ducpel-from-plist :floor) ducpel-direction))
  657. (cl-multiple-value-setq (ducpel-to-x ducpel-to-y)
  658. (ducpel-get-last-empty-xy ducpel-to-x ducpel-to-y
  659. ducpel-direction))
  660. (ducpel-set-cell ducpel-to-x ducpel-to-y
  661. :type ducpel-from-type
  662. :floor (plist-get ducpel-from-plist :floor))))
  663. (defun ducpel-move-box-to-empty ()
  664. "Try to move a box to an empty cell.
  665. If the move is possible, redraw the destination cell and
  666. return non-nil."
  667. (when (eql ducpel-to-type ducpel-empty)
  668. (when (eql (plist-get ducpel-from-plist :box)
  669. ducpel-teleport)
  670. (setq ducpel-undo-current-teleports ducpel-teleports)
  671. (push (list ducpel-to-x ducpel-to-y)
  672. ducpel-teleports))
  673. (ducpel-set-cell ducpel-to-x ducpel-to-y
  674. :type ducpel-floor
  675. :floor (plist-get ducpel-from-plist :box))))
  676. ;;; Undoing
  677. ;; To restore the previous state of the grid, we need to keep track of
  678. ;; changed cells, coordinates of the men and index of an active man.
  679. (defun ducpel-undo-reset-current ()
  680. "Reset current undo data to the default values."
  681. (setq ducpel-undo-current-cells nil
  682. ducpel-undo-current-teleports nil
  683. ducpel-undo-current-men (make-vector (length ducpel-men) nil)
  684. ducpel-undo-current-active-index nil))
  685. (defun ducpel-undo-init ()
  686. "Initialize undo data."
  687. (setq ducpel-undo-list nil)
  688. (ducpel-undo-reset-current))
  689. (defun ducpel-undo-save-current ()
  690. "Add undo info about the current move to `ducpel-undo-list'."
  691. (push (list ducpel-undo-current-cells
  692. ducpel-undo-current-men
  693. ducpel-undo-current-active-index
  694. ducpel-undo-current-teleports)
  695. ducpel-undo-list)
  696. (ducpel-undo-reset-current))
  697. (defun ducpel-undo-changes (cells men active teleports)
  698. "Undo changes from CELLS, MEN, ACTIVE and TELEPORTS.
  699. For the meaning of arguments, see `ducpel-undo-list'."
  700. (mapc (lambda (change)
  701. (apply 'gamegrid-set-cell change))
  702. cells)
  703. (dotimes (i (length men))
  704. (let ((man (aref men i)))
  705. (and man
  706. (aset ducpel-men i man))))
  707. (and active
  708. (setq ducpel-active-man-index active))
  709. (and teleports
  710. (setq ducpel-teleports teleports)))
  711. (defun ducpel-undo ()
  712. "Undo previous move or action."
  713. (interactive)
  714. ;; Undo possible switching of the men made since the last move
  715. (ducpel-undo-changes ducpel-undo-current-cells
  716. ducpel-undo-current-men
  717. ducpel-undo-current-active-index
  718. ducpel-undo-current-teleports)
  719. (ducpel-undo-reset-current)
  720. ;; Undo the last move
  721. (let ((move-changes (pop ducpel-undo-list)))
  722. (when move-changes
  723. (apply 'ducpel-undo-changes move-changes)
  724. (ducpel-remove-move)
  725. (pop ducpel-moves-history)
  726. (ducpel-check-done)
  727. (ducpel-print-done))))
  728. ;;; Replaying
  729. (defun ducpel-replay (&optional moves)
  730. "Replay MOVES.
  731. If MOVES is nil, use `ducpel-moves-history'."
  732. (interactive)
  733. (setq moves (reverse (or moves ducpel-moves-history)))
  734. (ducpel-restart-level)
  735. (dolist (move moves)
  736. (sit-for ducpel-replay-pause)
  737. (ducpel-set-active-man (car move))
  738. (ducpel-do (cadr move))))
  739. (defalias 'ducpel-replay-current 'ducpel-replay
  740. "Replay current moves.")
  741. (defun ducpel-replay-solution ()
  742. "Replay solution of the current level."
  743. (interactive)
  744. (if ducpel-solution
  745. (and (y-or-n-p "Do you REALLY want to see a solution of the level?")
  746. (ducpel-replay ducpel-solution))
  747. (message "No solution for the current map.")))
  748. (defun ducpel-replay-from-file (file)
  749. "Replay saved moves from FILE.
  750. Interactively, prompt for FILE."
  751. (interactive
  752. (list (read-file-name "Load replay from file: "
  753. ducpel-user-saves-directory)))
  754. (load file)
  755. (ducpel-replay))
  756. (defun ducpel-save-replay (file)
  757. "Save current moves to FILE.
  758. Interactively, prompt for FILE."
  759. (interactive
  760. (list (read-file-name "Save replay to file: "
  761. ducpel-user-saves-directory)))
  762. (or ducpel-moves-history
  763. (user-error "Do a single move at least"))
  764. (with-temp-buffer
  765. (insert ";; Saved moves for a ducpel level.\n"
  766. (format ";; Level file: %s\n\n" ducpel-level-file)
  767. "(setq ducpel-moves-history '")
  768. (princ ducpel-moves-history (current-buffer))
  769. (insert ")\n")
  770. (set (make-local-variable 'version-control) 'never)
  771. (write-file file t)))
  772. ;;; Display options
  773. (defvar ducpel-glyphs-function nil
  774. "Function returning alist of glyph specifications used in gamegrid.
  775. Associations of the alist should have the form:
  776. (PLIST . GLYPHS)
  777. PLIST is a unique cell property list, see `ducpel-cell-plists'.
  778. GLYPHS is a gamegrid specification for the PLIST.
  779. Gamegrid specifications are lists of the form:
  780. (GLYPH-SPEC FACE-SPEC COLOR-SPEC)
  781. They are used for `gamegrid-display-options' (see
  782. `gamegrid-initialize-display' for details).")
  783. ;; Avoid compilation warning about `ducpel-glyphs-default'
  784. (declare-function ducpel-glyphs-default "ducpel-glyphs" nil)
  785. (defun ducpel-get-glyphs ()
  786. "Return alist with glyph specifications."
  787. (if ducpel-glyphs-function
  788. (funcall ducpel-glyphs-function)
  789. (require 'ducpel-glyphs)
  790. (ducpel-glyphs-default)))
  791. (defun ducpel-get-display-options ()
  792. "Return array suitable for `gamegrid-display-options'."
  793. (let ((options (make-vector 256 nil))
  794. (glyph-alist (ducpel-get-glyphs)))
  795. (dolist (assoc glyph-alist)
  796. (aset options
  797. (apply 'ducpel-get-cell-char-by-plist (car assoc))
  798. (cdr assoc)))
  799. options))
  800. ;;; Printing info
  801. (defvar ducpel-print-level-line 1)
  802. (defvar ducpel-print-moves-line 2)
  803. (defvar ducpel-print-done-line 3)
  804. (defun ducpel-print-string (string dy)
  805. "Print STRING in the current gamegrid.
  806. DY is a number of line after `ducpel-height'."
  807. (goto-char (point-min))
  808. (let ((lines (forward-line (+ ducpel-height dy)))
  809. (inhibit-read-only t))
  810. ;; Go to the line even if it does not exist
  811. (insert (make-string lines ?\n))
  812. (delete-region (point) (line-end-position))
  813. (insert string)
  814. (and (eobp) (insert ?\n))))
  815. (defun ducpel-print-level ()
  816. "Print current level."
  817. (ducpel-print-string
  818. (format "Level: %s" (or ducpel-level ducpel-level-file))
  819. ducpel-print-level-line))
  820. (defun ducpel-print-moves ()
  821. "Print current count of moves."
  822. (ducpel-print-string
  823. (format "Moves: %d" ducpel-moves)
  824. ducpel-print-moves-line))
  825. (defun ducpel-print-done ()
  826. "Print current count of men."
  827. (ducpel-print-string
  828. (format "Done: %d/%d" ducpel-done (length ducpel-men))
  829. ducpel-print-done-line))
  830. (defun ducpel-print-info ()
  831. "Print all current info in the gamegrid."
  832. (ducpel-print-level)
  833. (ducpel-print-moves)
  834. (ducpel-print-done))
  835. ;;; Parsing levels
  836. (defvar ducpel-map-char-alist nil
  837. "Alist of characters used in level maps and cell plists.")
  838. (defvar ducpel-objects-char-alist nil
  839. "Alist of characters used in level maps for objects and cell plists.")
  840. (defun ducpel-init-map-char-alist ()
  841. "Fill `ducpel-map-char-alist' and `ducpel-objects-char-alist'."
  842. (setq ducpel-map-char-alist nil
  843. ducpel-objects-char-alist nil)
  844. (push (list ducpel-empty-map-char :type ducpel-empty)
  845. ducpel-map-char-alist)
  846. (push (list ducpel-wall-map-char :type ducpel-wall)
  847. ducpel-map-char-alist)
  848. (push (list ducpel-impassable-map-char :type ducpel-impassable)
  849. ducpel-map-char-alist)
  850. (push (list ducpel-man-map-char :type ducpel-man)
  851. ducpel-objects-char-alist)
  852. (push (list ducpel-active-man-map-char :type ducpel-active-man)
  853. ducpel-objects-char-alist)
  854. (dolist (floor ducpel-floor-types)
  855. (push (list (aref ducpel-floor-map-chars floor)
  856. :type ducpel-floor :floor floor)
  857. ducpel-map-char-alist))
  858. (dolist (box ducpel-floor-types)
  859. (push (list (aref ducpel-box-map-chars box)
  860. :type ducpel-box :box box)
  861. ducpel-objects-char-alist)))
  862. (ducpel-init-map-char-alist)
  863. (defun ducpel-get-cell-plist-by-map-chars (map-char obj-char)
  864. "Return cell type plist by MAP-CHAR and OBJ-CHAR characters."
  865. (let* ((map-plist (cdr (assoc map-char ducpel-map-char-alist)))
  866. (map-type (plist-get map-plist :type)))
  867. (cond
  868. ((eq map-type nil)
  869. (error "Wrong map character: %c" map-char))
  870. ((eql map-type ducpel-floor)
  871. (let* ((obj-plist (cdr (assoc obj-char ducpel-objects-char-alist)))
  872. (obj-type (plist-get obj-plist :type)))
  873. (cond
  874. ((eql obj-type ducpel-box)
  875. (list :type obj-type
  876. :floor (plist-get map-plist :floor)
  877. :box (plist-get obj-plist :box)))
  878. ((or (eql obj-type ducpel-man)
  879. (eql obj-type ducpel-active-man))
  880. (list :type obj-type
  881. :floor (plist-get map-plist :floor)))
  882. (t map-plist))))
  883. (t map-plist))))
  884. (defun ducpel-get-cell-char-by-map-chars (map-char obj-char)
  885. "Return cell type character by MAP-CHAR and OBJ-CHAR characters."
  886. (apply 'ducpel-get-cell-char-by-plist
  887. (ducpel-get-cell-plist-by-map-chars map-char obj-char)))
  888. (defun ducpel-parse-solution ()
  889. "Parse solution of the level in the current buffer.
  890. Return solution (list of moves) or nil if solution is not found."
  891. (goto-char (point-min))
  892. (when (re-search-forward ducpel-solution-re nil t)
  893. (re-search-forward "(")
  894. (backward-char)
  895. (let ((beg (point))
  896. (end (progn (forward-sexp) (point))))
  897. (read (buffer-substring-no-properties beg end)))))
  898. (defun ducpel-parse-map (re)
  899. "Parse level map in the current buffer.
  900. Search for regexp RE and parse the level map after it.
  901. Return list of lines."
  902. (goto-char (point-min))
  903. (re-search-forward re)
  904. (forward-line)
  905. (while (looking-at ducpel-ignored-line-re)
  906. (forward-line))
  907. (let ((beg (point))
  908. (end (if (re-search-forward ducpel-ignored-line-re nil t)
  909. (progn (beginning-of-line) (point))
  910. (point-max))))
  911. (split-string (buffer-substring-no-properties beg end) "\n" t)))
  912. (defun ducpel-init-level-data (file)
  913. "Read ducpel level map from FILE.
  914. Set the following variables: `ducpel-level-data',
  915. `ducpel-width', `ducpel-height', `ducpel-solution'."
  916. (with-temp-buffer
  917. (insert-file-contents-literally file)
  918. (setq ducpel-solution (ducpel-parse-solution))
  919. (let ((map (ducpel-parse-map ducpel-map-re))
  920. (objects (ducpel-parse-map ducpel-objects-re))
  921. (height 0)
  922. (width 0))
  923. ;; Define height and width of the data array
  924. (dolist (line map)
  925. (cl-incf height)
  926. (let ((w (length line)))
  927. (when (> w width)
  928. (setq width w))))
  929. (setq ducpel-level-data (make-vector height nil)
  930. ducpel-width width
  931. ducpel-height height)
  932. ;; Fill the data array
  933. (cl-loop for map-line in map
  934. for objects-line in objects
  935. for y from 0
  936. do (let ((line (make-vector width nil)))
  937. (cl-loop for map-char across map-line
  938. for obj-char across objects-line
  939. for x from 0
  940. do (aset line x
  941. (ducpel-get-cell-char-by-map-chars
  942. map-char obj-char)))
  943. (aset ducpel-level-data y line))))))
  944. (defun ducpel-init-buffer ()
  945. "Fill current buffer with the level map.
  946. Set `ducpel-men', `ducpel-active-man-index' and
  947. `ducpel-teleports' variables."
  948. (gamegrid-init-buffer ducpel-width ducpel-height ?\s)
  949. (setq ducpel-teleports nil)
  950. (let (men)
  951. (dotimes (y ducpel-height)
  952. (dotimes (x ducpel-width)
  953. (let ((char (aref (aref ducpel-level-data y) x)))
  954. (when char
  955. (let* ((plist (ducpel-get-cell-plist-by-cell-char char))
  956. (type (plist-get plist :type)))
  957. (cond
  958. ((eql type ducpel-man)
  959. (push (list x y) men))
  960. ((eql type ducpel-active-man)
  961. (push (list x y) men)
  962. (setq ducpel-active-man-index
  963. (- (length men) 1)))
  964. ((eql (plist-get plist :floor) ducpel-teleport)
  965. (push (list x y) ducpel-teleports))))
  966. (gamegrid-set-cell x y char)))))
  967. (setq ducpel-men
  968. (apply 'vector (nreverse men)))))
  969. ;;; UI for levels
  970. (defun ducpel-restart-level (&optional reload)
  971. "Restart current level.
  972. If RELOAD is non-nil (interactively with prefix), reread current
  973. level map from the level file."
  974. (interactive "P")
  975. (when reload
  976. (ducpel-init-level-data ducpel-level-file))
  977. (ducpel-init-buffer)
  978. (ducpel-undo-init)
  979. (setq ducpel-moves 0
  980. ducpel-done 0
  981. ducpel-moves-history nil)
  982. (ducpel-print-info))
  983. (defun ducpel-get-file-by-level (level)
  984. "Return file name by LEVEL number."
  985. (expand-file-name (format "%04d" level) ducpel-levels-directory))
  986. (defun ducpel-goto-level (level)
  987. "Go to a specified LEVEL."
  988. (interactive "NLevel: ")
  989. (let ((file (ducpel-get-file-by-level level)))
  990. (or (file-regular-p file)
  991. (error "Level %d does not exist yet" level))
  992. (setq ducpel-level level
  993. ducpel-level-file file)
  994. (ducpel-restart-level t)))
  995. (defun ducpel-next-level ()
  996. "Go to the next level."
  997. (interactive)
  998. (ducpel-goto-level
  999. (if ducpel-level (+ ducpel-level 1) ducpel-default-level)))
  1000. (defun ducpel-previous-level ()
  1001. "Go to the previous level."
  1002. (interactive)
  1003. (ducpel-goto-level
  1004. (if ducpel-level (- ducpel-level 1) ducpel-default-level)))
  1005. (defun ducpel-load-level-from-file (file)
  1006. "Load level map from FILE."
  1007. (interactive
  1008. (list (read-file-name "Load ducpel map: "
  1009. ducpel-user-levels-directory)))
  1010. (setq ducpel-level nil
  1011. ducpel-level-file file)
  1012. (ducpel-restart-level t))
  1013. ;;; Misc
  1014. (defun ducpel-get-index-by-shift (len index shift)
  1015. "Return index of element of array or list by shifting INDEX by SHIFT.
  1016. LEN is a length of array or list."
  1017. (mod (+ index shift) len))
  1018. (defun ducpel-get-index-by-element (array-or-list elt &optional cmp noerror)
  1019. "Return index of element ELT from ARRAY-OR-LIST.
  1020. Compare ELT with elements of ARRAY-OR-LIST using CMP
  1021. function (`eq' by default).
  1022. If NOERROR is non-nil, return nil if ELT is not found; otherwise
  1023. signal an error."
  1024. (or cmp
  1025. (setq cmp 'eq))
  1026. (let (type)
  1027. (or (cond
  1028. ((listp array-or-list)
  1029. (setq type "list")
  1030. (cl-loop for obj in array-or-list
  1031. for i from 0
  1032. if (funcall cmp elt obj) return i))
  1033. ((arrayp array-or-list)
  1034. (setq type "array")
  1035. (cl-loop for i below (length array-or-list)
  1036. if (funcall cmp elt (aref array-or-list i)) return i))
  1037. (t (error "Should be array or list")))
  1038. (and (null noerror)
  1039. (error "Element %s is not found in %s" elt type)))))
  1040. ;;; Major mode
  1041. (defvar ducpel-mode-map
  1042. (let ((map (make-sparse-keymap)))
  1043. (define-key map "R" 'ducpel-restart-level)
  1044. (define-key map "F" 'ducpel-load-level-from-file)
  1045. (define-key map "L" 'ducpel-goto-level)
  1046. (define-key map "N" 'ducpel-next-level)
  1047. (define-key map "P" 'ducpel-previous-level)
  1048. (define-key map "u" 'ducpel-undo)
  1049. (define-key map "\C-_" 'ducpel-undo)
  1050. (define-key map [(control ?/)] 'ducpel-undo)
  1051. (define-key map "\t" 'ducpel-next-man)
  1052. (define-key map "\e\t" 'ducpel-previous-man)
  1053. (define-key map [backtab] 'ducpel-previous-man)
  1054. (define-key map " " 'ducpel-action)
  1055. (define-key map "b" 'ducpel-move-left)
  1056. (define-key map "f" 'ducpel-move-right)
  1057. (define-key map "p" 'ducpel-move-up)
  1058. (define-key map "n" 'ducpel-move-down)
  1059. (define-key map [left] 'ducpel-move-left)
  1060. (define-key map [right] 'ducpel-move-right)
  1061. (define-key map [up] 'ducpel-move-up)
  1062. (define-key map [down] 'ducpel-move-down)
  1063. (define-key map "rc" 'ducpel-replay-current)
  1064. (define-key map "rf" 'ducpel-replay-from-file)
  1065. (define-key map "rS" 'ducpel-replay-solution)
  1066. (define-key map "rs" 'ducpel-save-replay)
  1067. map)
  1068. "Keymap for `ducpel-mode'.")
  1069. (define-derived-mode ducpel-mode special-mode "Ducpel"
  1070. "Major mode for playing ducpel.
  1071. \\{ducpel-mode-map}"
  1072. (set (make-local-variable 'gamegrid-use-glyphs) ducpel-use-glyphs)
  1073. ;; hl-line disturbs if `ducpel-use-glyphs' is nil
  1074. (set (make-local-variable 'global-hl-line-mode) nil)
  1075. (gamegrid-init (ducpel-get-display-options)))
  1076. ;;;###autoload
  1077. (defun ducpel ()
  1078. "Play ducpel game."
  1079. (interactive)
  1080. (let ((buf (get-buffer ducpel-buffer-name)))
  1081. (pop-to-buffer-same-window ducpel-buffer-name)
  1082. (unless buf
  1083. (ducpel-mode)
  1084. (ducpel-goto-level ducpel-default-level))))
  1085. (provide 'ducpel)
  1086. ;;; ducpel.el ends here