unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#8758: New package: ninja
@ 2011-05-29 21:34 Matthew Styskal
  2011-05-30 20:48 ` bug#8758: Fixed-width font Matthew Styskal
  2012-04-12 19:40 ` bug#8758: New package: ninja Lars Magne Ingebrigtsen
  0 siblings, 2 replies; 8+ messages in thread
From: Matthew Styskal @ 2011-05-29 21:34 UTC (permalink / raw)
  To: 8758


[-- Attachment #1.1: Type: text/plain, Size: 808 bytes --]

I apologize if this is the wrong place to submit this; it seems somewhat odd to submit new packages to a bug-fixing mailgroup, but that's what the Emacs homepage on the GNU website said to do. (I'm gnu here, if you'll pardon the pun.)



One of the shortcomings Emacs seemed to have to me was its lack of original action games; it has implementations of classics like Pong and Snake, but no games of its own. So I wrote one (see the attachment). It has a pretty stiff learning curve, but hey, so does Emacs.



I attempted to follow the header & docstring conventions, and I should have plenty of free time soon to make any other improvements to this you guys deem necessary before its inclusion. And I am willing to maintain it and to give the copyright to the FSF.



Thank you for your time.

[-- Attachment #1.2: Type: text/html, Size: 1246 bytes --]

[-- Attachment #2: ninja.el --]
[-- Type: application/octet-stream, Size: 13113 bytes --]

;;; ninja.el -- an action-arcade game where you jump to and from falling blocks

;; Copyright (C) 2011 Matthew Styskal

;; Author: Matthew Styskal <mastyskal@my.lonestar.edu>
;; Created: x Aug 2010
;; Version: 1.0
;; Keywords: game, arcade, ninja
;; Author's High Score: 1147


;;; Commentary:

;;Ninja is an original action-arcade game for Emacs. The object of the game
;;is simply to survive as long as possible; travelling off any of the four
;;edges of the screen results in death. Jump from block to block with the keys:

;;    G H
;;C V     N M
;;G and H give high-arching jumps, V and N give more line-drive jumps,
;;and C and M dive downwards.

;;600 or so is a decent score. 1000 is a very good score.


;;; Code:

;(require 'gamegrid) ;more fun without it (timer stuff wasn't working anyway)
(require 'cl)

(setq ninja-timer nil) ;most variables are declared in ninja. This is here so
                       ;the timer from an older game can be stopped in ninja.

(defconst ninja-width 70) ;old value was 60... which plays better?
(defconst ninja-height 30)
(defconst ninja-tick 0.06) ;60, ninja-wait. 40000, -alt. 0.06, timers.
(defconst ninja-block-speed-range-size 6) ;0.1 * [positive int <= this] = speed
(defconst ninja-max-block-age (ceiling (* (+ 2 ninja-height) 10)))
(defconst ninja-block-width 5)
(defconst ninja-columns (floor (/ ninja-width (* 2 ninja-block-width))))
(defconst ninja-max-blocks (* 3 ninja-columns)) ;only on-screen ones count 
(defconst ninja-min-blocks (ceiling (/ ninja-max-blocks 2)))
(defconst ninja-max-per-frame (floor (/ ninja-columns 2)))
(defconst ninja-block-spawn-odds 9)
;;Physics constants:
(defconst ninja-max-vy 4)
(defconst ninja-start-vy -1.6)
(defconst ninja-gravity 0.05)
(defconst ninja-jumps-vx (list 0 -0.9  -0.6  0.6  0.9  -0.3 0.3))
(defconst ninja-jumps-vy (list 0 -0.55 -0.8 -0.8 -0.55  0.3 0.3))
(defun ninja-hardcoded-dives () "To make the last two \"jumps\" work..."
  (if (= ninja-key 5) (setq ninja-x (- ninja-x 5)) nil)
  (if (= ninja-key 6) (setq ninja-x (+ ninja-x 5)) nil))

(defvar ninja-keymap (make-sparse-keymap 'ninja-keymap))
(define-key ninja-keymap "v" 'ninja-move-v)
(define-key ninja-keymap "g" 'ninja-move-g)
(define-key ninja-keymap "h" 'ninja-move-h)
(define-key ninja-keymap "n" 'ninja-move-n)
(define-key ninja-keymap "c" 'ninja-move-c)
(define-key ninja-keymap "m" 'ninja-move-m)
(define-key ninja-keymap "q" 'ninja-halt)
;;Should be done with parameters instead of 6 functions, if possible...
(defun ninja-move-v () "" (interactive) (setq ninja-key 1))
(defun ninja-move-g () "" (interactive) (setq ninja-key 2))
(defun ninja-move-h () "" (interactive) (setq ninja-key 3))
(defun ninja-move-n () "" (interactive) (setq ninja-key 4))
(defun ninja-move-c () "" (interactive) (setq ninja-key 5))
(defun ninja-move-m () "" (interactive) (setq ninja-key 6))

(defun ninja-halt () "Stops the timer, clears the graphics, shows the score."
  (interactive)
  (cancel-timer ninja-timer)
  (erase-buffer)
  (insert "Your lifespan was ")
  (insert (number-to-string ninja-lifespan))
  (insert ".\n\n\nM-x ninja starts another game."))

(defun ninja-coord-to-char (x y) "Convert coordinates to a char index."
  (+ (floor x) (* (+ 1 ninja-width) (floor y))))

(defun ninja-wait (n) "Waits n milliseconds. n should be < 500."
  (setq ninja-start (nth 2 (current-time)))
  (while (< (abs (- (nth 2 (current-time)) ninja-start)) (* n 1000))
    (+ 100 10)))

(defun ninja-wait-alt (n) "Goes around wasting time n times."
  (while (not (= n 0))
    (/ 100 10)
    (setq n (- n 1))))

(defun ninja-pick-column () 
"Randomly chooses a column, without repeating any one column before all the 
columns are picked in an iteration."
  (setq ninja-try (random ninja-columns))
  (setq ninja-dir (if (= 0 (random 2)) 1 -1))
  (while (nth ninja-try ninja-column-used-ps)
    (setq ninja-try (+ ninja-try ninja-dir))
    (if (< ninja-try 0) (setq ninja-try (- ninja-columns 1)) nil)
    (if (>= ninja-try ninja-columns) (setq ninja-try 0) nil))
  (setf (nth ninja-try ninja-column-used-ps) t)
  (setq ninja-full-p t)
  (setq ninja-i 0)
  (while (< ninja-i ninja-columns)
    (if (nth ninja-i ninja-column-used-ps) nil (setq ninja-full-p nil))
    (setq ninja-i (+ 1 ninja-i)))
  (setq ninja-i 0)
  (if ninja-full-p
      (while (< ninja-i ninja-columns)
        (setf (nth ninja-i ninja-column-used-ps) nil)
        (setq ninja-i (+ 1 ninja-i)))
      nil)
  ninja-try)

(defun ninja-update-blocks ()
"Deal with blocks. Randomly spawn blocks in random columns with random speed.
Then push them onto the queues. Write over each existing block with spaces, 
then move each existing block. Draw the ones on the screen, and pop the ones
at the end of their lifespan off the queue."
  (setq ninja-spawned 0)
  (while (and (or (> ninja-min-blocks ninja-block-count) 
                  (= 0 (random (- ninja-block-spawn-odds 1))))
              (> ninja-max-blocks ninja-block-count)
              (> ninja-max-per-frame ninja-spawned))
    ;;using append and ' (or `..., as necessary) instead of nconc and list 
    ;;caused gor'am failures.
    (setq ninja-blocks-y (nconc ninja-blocks-y (list 0.0)))
    (setq ninja-blocks-vy (nconc ninja-blocks-vy ;this must be changed if speeds
        (list (/ (+ 1.0 (random ninja-block-speed-range-size)) 10))))    ;change
    (setq ninja-blocks-age (nconc ninja-blocks-age (list 0))) 
    (setq ninja-blocks-x (nconc ninja-blocks-x 
        (list (+ 1 (* 10 (ninja-pick-column))))))
    (setq ninja-block-count (+ 1 ninja-block-count))
    (setq ninja-spawned (+ 1 ninja-spawned)))
  
  (while (< ninja-max-block-age (car ninja-blocks-age))
    (setq ninja-blocks-y (cdr ninja-blocks-y))
    (setq ninja-blocks-vy (cdr ninja-blocks-vy))
    (setq ninja-blocks-x (cdr ninja-blocks-x))
    (setq ninja-blocks-age (cdr ninja-blocks-age))
    (setq ninja-standing-block (- ninja-standing-block 1))
    (setq ninja-last-block (- ninja-last-block 1)))
  
  (setq ninja-i 0)
  (setq ninja-on-screen-p nil)
  (setq ninja-char " ")
  (while (< ninja-i (length ninja-blocks-y))
    (if (< (floor (nth ninja-i ninja-blocks-y)) ninja-height)
        (progn 
          (setq ninja-on-screen-p t)
          (goto-char (ninja-coord-to-char (nth ninja-i ninja-blocks-x) 
                                          (nth ninja-i ninja-blocks-y)))
          (delete-char ninja-block-width)
          (dotimes (ninja-throwaway ninja-block-width ninja-throwaway) 
            (insert ninja-char)))
        (if ninja-on-screen-p ;on screen before movement but not now
            (setq ninja-block-count (- ninja-block-count 1)) 
            nil))
    (if (string= ninja-char " ")
        (progn
          (setf (nth ninja-i ninja-blocks-y) 
              (+ (nth ninja-i ninja-blocks-y) (nth ninja-i ninja-blocks-vy)))
          (setq ninja-char "#"))
        (progn
          (setq ninja-char " ")
          (setq ninja-on-screen-p nil)
          (setf (nth ninja-i ninja-blocks-age)
              (+ 1 (nth ninja-i ninja-blocks-age)))
          (setq ninja-i (+ ninja-i 1))))))

(defun ninja-draw-player (undraw-p)
"Draw the appropriate player sprite."
  (goto-char (ninja-coord-to-char ninja-x ninja-y))
  (delete-char 1)
  (insert (if undraw-p " " "^"))
  (previous-line)
  (backward-char)
  (delete-char 1)
  (insert (if undraw-p " " (if (>= ninja-standing-block 0) ";" "Y"))))

(defun ninja-collision-checking (i)
"Returns the index of the block hit if there is one; else returns nil.
Currently a primitive version; later versions should check for intersecting
vectors instead of merely intersecting existence."
  (if (= i (length ninja-blocks-y))
      nil
      (if (and (not (= i ninja-last-block))
                    ;;landing on the last block is undesired behavior, as there
                    ;;are no vertical jumps; the user experiences no jump
               (>= ninja-x (nth i ninja-blocks-x))
               (< (floor ninja-x) (+ ninja-block-width (nth i ninja-blocks-x)))
               (or (= (floor ninja-y) (floor (nth i ninja-blocks-y)))
                   (= (+ 1 (floor ninja-y)) (floor (nth i ninja-blocks-y)))
                   (= (- (floor ninja-y) 1) (floor (nth i ninja-blocks-y)))))
          i
          (ninja-collision-checking (+ i 1)))))

(defun ninja-update-player ()
"Rough pre-writing psuedocode; not ordered the way the code is:
Undraw and check if ninja-standing-block is nonnegative. If so, move to the
center of that block and check whether the character is onscreen. If so,
check input. If there is none, draw the character. If there is input, set
ninja-standing-block to -1, clear the input, and start the jump.
If the character was offscreen at that check, end the game. If the character
was not standing on a block at the beginning of the turn, continue the jump
and check if he is now upon a block. If he is, set ninja-standing-block to
it and call this function again. If he is not, draw the flying sprite at his
location, provided that's onscreen. If it isn't, end the game."
  (if (or (> (floor ninja-y) (- ninja-height 1))
          (< (floor ninja-y) 1)
          (< ninja-x 0)
          (> ninja-x (- ninja-width 1)))
      (ninja-halt)
      (progn
        (ninja-draw-player t)
        (if (>= ninja-standing-block 0)
            (progn
              (setq ninja-last-block ninja-standing-block)
              (setq ninja-x (+ (nth ninja-standing-block ninja-blocks-x)
                               (floor (/ ninja-block-width 2))))
              (setq ninja-y (- (nth ninja-standing-block ninja-blocks-y) 1))
              (if (= ninja-key 0)
                  nil
                  (progn
                    (setq ninja-vx (nth ninja-key ninja-jumps-vx))
                    (setq ninja-vy (nth ninja-key ninja-jumps-vy))
                    (setq ninja-standing-block -1)
                    (ninja-hardcoded-dives) 
                    (setq ninja-key 0))))
            (progn
              (setq ninja-key 0)
              (setq ninja-x (+ ninja-vx ninja-x))
              (setq ninja-y (+ ninja-y ninja-vy))
              (setq ninja-vy (+ ninja-vy ninja-gravity))
              (if (> ninja-vy ninja-max-vy)
                  (setq ninja-vy ninja-max-vy)
                  nil)
              (setq ninja-collision (ninja-collision-checking 0))
              (if ninja-collision
                  (progn
                    (setq ninja-standing-block ninja-collision)
                    (ninja-update-player))
                  nil)))
        (ninja-draw-player nil))))

;(defun ninja-game-loop () "Currently a test, not a game."
;  (interactive)
;  (delete-char 1)
;  (insert "!")
;  (forward-line 1))

(defun ninja-game-loop () "The Loop of the Game."
  (unless (string= (buffer-name) "*ninja*")
    (cancel-timer ninja-timer))
  (ninja-update-blocks)
  (ninja-update-player)
  (setq ninja-lifespan (+ ninja-lifespan 1)) 
  (goto-char 1) ;comment out and the ninja has a spear or something 
                ;(when the cursor is a single line)
  ;(sit-for 0) ;redisplay isn't in Emacs 21; this does weird things on input
  (redisplay t))

(defun ninja-auto-game-loop ()
"Not called; call from ninja for another, inferior timing ed::method."
  ;(sit-for 0.3) ;finishes on input
  (ninja-wait ninja-tick) ;blocks input
  (ninja-game-loop)
  (ninja-auto-game-loop)) ;Will crash eventually

(defun ninja () "Start a new game of ninja."
  (interactive)
  (switch-to-buffer "*ninja*")
  (erase-buffer)
  ;(kill-all-local-variables)
  ;(setq max-lisp-eval-depth 100000) ;Uncomment if ninja-auto-game-loop is used
  (setq undo-outer-limit 30000000) ;Why is this here again?
  (setq ninja-throwaway nil)
  (setq ninja-lifespan 0)
  (setq ninja-key 0)
  (setq ninja-x (- (/ ninja-width 2) (/ ninja-block-width 2)))
  (setq ninja-y (- ninja-height 1.0))
  (setq ninja-vx 0.0)
  (setq ninja-vy ninja-start-vy)
  (setq ninja-standing-block -1)
  (setq ninja-last-block -1)
  (setq ninja-blocks-y nil) ;y coord is 0-based from top
  (setq ninja-blocks-vy nil)
  (setq ninja-blocks-x nil) ;x coord is 1-based from left
  (setq ninja-blocks-age nil)
  (setq ninja-block-count 0)
  (setq ninja-column-used-ps nil)
  (dotimes (ninja-throwaway ninja-columns ninja-throwaway)
    (setq ninja-column-used-ps (nconc ninja-column-used-ps (list nil))))
  (dotimes (ninja-throwaway ninja-height ninja-throwaway) (progn
    (dotimes (ninja-throwaway ninja-width ninja-throwaway) (insert " "))
    (insert "\n")))
  (goto-char 1)
  (use-local-map ninja-keymap)
  ;(gamegrid-kill-timer)
  ;(gamegrid-start-timer ninja-tick 'ninja-game-loop) ;didn't work
  ;(ninja-auto-game-loop) ;didn't play nicely with input
  (if ninja-timer 
      (cancel-timer ninja-timer) 
      nil)
  (setq ninja-timer (run-at-time "0 sec" ninja-tick 'ninja-game-loop)))

(provide 'ninja)

;;; ninja.el ends here

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2016-02-26  1:49 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-05-29 21:34 bug#8758: New package: ninja Matthew Styskal
2011-05-30 20:48 ` bug#8758: Fixed-width font Matthew Styskal
2012-04-12 19:40 ` bug#8758: New package: ninja Lars Magne Ingebrigtsen
2012-04-12 21:42   ` Stefan Monnier
2012-04-12 22:05     ` Matthew Styskal
2016-02-25  6:54       ` Lars Ingebrigtsen
2016-02-25 22:59         ` matthew
2016-02-26  1:49           ` Stefan Monnier

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).