Visual LISP, AutoLISP and General Customization
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

A quick game of Snake inside AutoCAD - enjoy!

2 REPLIES 2
Reply
Message 1 of 3
doaiena
2139 Views, 2 Replies

A quick game of Snake inside AutoCAD - enjoy!

This is a proof of concept, showing user interaction inside a while loop. The magic happens in a .NET module, inspired by Kean Walmsley https://www.keanw.com/2007/02/allowing_users_.html. I also made use of Lee Mac's random number functions http://www.lee-mac.com/random.html . I haven't tested the game extensively, so bugs are possible. Feel free to adjust/optimize the code as needed. I've compiled a .dll for the users that can't compile it themselves /.NET Framework 4.6/.

To run the game, copy the "GetInput.dll" file in an AutoCAD trusted folder, then load the "Snake.lsp" file. The command to run the game is c:Snake.

Controls:
W S A D - UP DOWN LEFT RIGHT
Q - Exit the game

Have fun!

.NET code:

using System;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using WinForms = System.Windows.Forms;


namespace ACAD_Test
{
    public class MyCommands
    {
        [LispFunction("GetInput")]
        static public void GetInput(ResultBuffer resbuf)
        {
            MyMessageFilter filter = new MyMessageFilter();
            System.Windows.Forms.Application.AddMessageFilter(filter);
            try
            {
                System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
                sw.Reset();
                sw.Start();
                Document doc = Application.DocumentManager.MdiActiveDocument;
                Editor ed = doc.Editor;

                TypedValue[] args = resbuf.AsArray();
                double timeout = (double)args[0].Value;

                while (sw.ElapsedMilliseconds < timeout)
                {
                    // 5s timeout for the function, if something goes wrong
                    if ((sw.ElapsedMilliseconds / 1000.0) > 5)
                    {
                        break;
                    }

                    System.Windows.Forms.Application.DoEvents();

                    if (filter.bCanceled == true)
                    {
                        break;
                    }

                    switch (filter.key)
                    {
                        case "A":
                            doc.SetLispSymbol("keyPress", new TypedValue((int)LispDataType.Text, "A"));
                            break;
                        case "D":
                            doc.SetLispSymbol("keyPress", new TypedValue((int)LispDataType.Text, "D"));
                            break;
                        case "S":
                            doc.SetLispSymbol("keyPress", new TypedValue((int)LispDataType.Text, "S"));
                            break;
                        case "W":
                            doc.SetLispSymbol("keyPress", new TypedValue((int)LispDataType.Text, "W"));
                            break;
                        case "Q":
                            doc.SetLispSymbol("keyPress", new TypedValue((int)LispDataType.Text, "Q"));
                            break;
                    }

                    if (filter.key != "")
                    {
                        break;
                    }
                }
                System.Windows.Forms.Application.RemoveMessageFilter(filter);
            }
            catch (System.Exception Ex)
            {
                System.Windows.Forms.Application.RemoveMessageFilter(filter);
                Application.ShowAlertDialog("System exception:\n" + Ex.Message + "\n" + Ex.Source + "\n" + Ex.StackTrace);
            }
        }
    }//Class

    public class MyMessageFilter : WinForms.IMessageFilter
    {
        public const int WM_KEYDOWN = 0x0100;
        public bool bCanceled = false;
        public string key = "";

        public bool PreFilterMessage(ref WinForms.Message m)
        {
            if (m.Msg == WM_KEYDOWN)
            {
                // Check for the Escape keypress
                WinForms.Keys kc = (WinForms.Keys)(int)m.WParam & WinForms.Keys.KeyCode;

                if (kc == WinForms.Keys.Escape)
                {
                    bCanceled = true;
                }
                key = kc.ToString();
                // Return true to filter all keypresses
                return true;
            }
            key = "";
            // Return false to let other messages through
            return false;
        }
    }//Class
}//Namespace





AutoLisp code:

;;;	Autoload .NET module		
(if (not (= (type GetInput) 'EXRXSUBR))
(if (findfile "GetInput.dll")
(if (vl-catch-all-error-p (vl-catch-all-apply 'eval (list (list 'vl-cmdf "netload" (findfile "GetInput.dll")))))
(alert "There was a problem loading \"GetInput.dll\".")
)
(alert "\"GetInput.dll\" was not found. \nPlease copy \"GetInput.dll\" to an AutoCAD trusted folder and reload Snake.lsp.")
)
)
;;;	Autoload .NET module		

(defun c:Snake ( / *error* RemoveEnts GameOver MoveSnake DoesSnakeEat SnakeCanMove SpawnFood AddNode LWPoly Point LM:randrange LM:rand $xn
		canvas canvasX canvasY currentNode direction food increment keyPress lastNode nextNode node nodeSize snake stop tick timer vars)

(setq *variables* '(RemoveEnts GameOver MoveSnake DoesSnakeEat SnakeCanMove SpawnFood AddNode LWPoly Point LM:randrange LM:rand $xn
		canvas canvasX canvasY currentNode direction food increment keyPress lastNode nextNode node nodeSize snake stop tick timer vars))

(defun *error* (msg)
(cond
((not (vl-position msg '("Function cancelled" "*Cancel*" "quit / exit abort")))
(princ "\nAn error has occured with Snake.")
)
(T (princ "\nSnake game has ended."))
)

(RemoveEnts)

(if (/= vars nil) (mapcar '(lambda (x)
(if (eval (read (car x))) (setvar (car x) (eval (read (car x)))))
(set (read (car x)) nil)) vars))

(if (/= *variables* nil) (mapcar '(lambda (x) (set x nil)) *variables*))
(setq *variables* nil)

(redraw)

(gc)
(princ)
);defun

(defun RemoveEnts (/)
(mapcar '(lambda (ent) (if (entget ent) (entdel ent))) (append snake (list food canvas)))
);defun

;; Rand  -  Lee Mac
;; PRNG implementing a linear congruential generator with
;; parameters derived from the book 'Numerical Recipes'

(defun LM:rand ( / a c m )
    (setq m   4294967296.0
          a   1664525.0
          c   1013904223.0
          $xn (rem (+ c (* a (cond ($xn) ((getvar 'date))))) m)
    )
    (/ $xn m)
);defun

;; Random in Range  -  Lee Mac
;; Returns a pseudo-random integral number in a given range (inclusive)

(defun LM:randrange ( a b )
    (+ (min a b) (fix (* (LM:rand) (1+ (abs (- a b))))))
);defun

(defun Point (pt)
(entmakex (list (cons 0 "POINT") (cons 10 pt)))
);defun

(defun LWPoly (lst)
(entmakex (append (list (cons 0 "LWPOLYLINE")
(cons 100 "AcDbEntity")
(cons 100 "AcDbPolyline")
(cons 90 (length lst))
(cons 70 1))
(mapcar (function (lambda (p) (cons 10 p))) lst)))
);defun

(defun AddNode (/)
(setq snake (append snake (list (Point (cdr lastNode)))))
);defun

(defun SpawnFood (/)
(Point (list (* (LM:randrange 0.5 (- canvasX 1)) nodeSize) (* (LM:randrange 0.5 (- canvasY 1)) nodeSize)))
);defun

(defun SnakeCanMove ( / newPoint)
(setq newPoint (mapcar '+ (assoc 10 (entget (car snake))) increment))
(if (and (> (cadr newPoint) 0) (< (cadr newPoint) (* canvasX nodeSize))
	 (> (caddr newPoint) 0) (< (caddr newPoint) (* canvasY nodeSize))
	 (not (vl-position newPoint (mapcar '(lambda (node) (assoc 10 (entget node))) snake))))
T
(GameOver)
)
);defun

(defun DoesSnakeEat ( / newPoint)
(setq newPoint (mapcar '+ (assoc 10 (entget (car snake))) increment))
(if (equal (assoc 10 (entget food)) newPoint)
(progn
(entdel food)
(AddNode)
(setq food (SpawnFood))
(entmod (append (entget food) (list (cons 62 3))));food color
))
);defun

(defun MoveSnake ( / move)
(cond
((= direction "UP") (setq increment (list 0 0 nodeSize 0)) (if (SnakeCanMove) (progn (setq move T) (DoesSnakeEat))));UP
((= direction "DOWN") (setq increment (list 0 0 (* nodeSize -1) 0)) (if (SnakeCanMove) (progn (setq move T) (DoesSnakeEat))));DOWN
((= direction "LEFT") (setq increment (list 0 (* nodeSize -1) 0 0)) (if (SnakeCanMove) (progn (setq move T) (DoesSnakeEat))));LEFT
((= direction "RIGHT") (setq increment (list 0 nodeSize 0 0)) (if (SnakeCanMove) (progn (setq move T) (DoesSnakeEat))));RIGHT
);cond

(if move
(progn
(setq nextNode (mapcar '+ (assoc 10 (entget (car snake))) increment))
(mapcar '(lambda (node / currentNode)
(setq currentNode (assoc 10 (entget node)))
(entmod (subst nextNode currentNode (entget node)))
(setq nextNode currentNode)
)
snake
)
))
);defun

(defun GameOver (/)
(setq stop T)
(alert "Game Over!")
);defun

;;;	Function Starts Here		
(if (not (= (type GetInput) 'EXRXSUBR))
(alert "\"GetInput.dll\" is not loaded.")
(progn

(alert (strcat "Controls:"
"\nW - Up"
"\nS - Down"
"\nA - Left"
"\nD - Right"
"\n\nQ - End the game"

"\n\n\nIf you don't want to see this message at the start of the game, remove the dialog from Snake.lsp"
))

(setq canvasX 20)
(setq canvasY 15)
(setq nodeSize 10)
(setq direction "RIGHT")
(setq tick 0.2)
(setq stop nil)

(setq vars (list (list "OSMODE" 0) (list "PDMODE" 97) (list "PDSIZE" nodeSize)))
(mapcar '(lambda (var)
(set (read (car var)) (getvar (car var)))
(setvar (car var) (cadr var))) vars)

(command "_ucs" "_w")
(setq canvas (LWPoly (list (list 0 0 0)
			   (list (* canvasX nodeSize) 0 0)
			   (list (* canvasX nodeSize) (* canvasY nodeSize) 0)
			   (list 0 (* canvasY nodeSize) 0))))
(entmod (append (entget canvas) (list (cons 62 1))));canvas color
(setq snake (list (Point (list (* nodeSize 2.5) (/ nodeSize 2))) (Point (list (* nodeSize 1.5) (/ nodeSize 2))) (Point (list (/ nodeSize 2) (/ nodeSize 2)))))
(setq lastNode (assoc 10 (entget (last snake))))
(setq food (SpawnFood))
(entmod (append (entget food) (list (cons 62 3))));food color
(command "_-view" "_t")
(command "_zoom" "o" canvas "")

(setq timer (getvar "Millisecs"))
(while (not stop)

(setq keypress nil)
(GetInput (- (* tick 1000.0) (- (getvar "Millisecs") timer)))
(cond
((= keypress "Q") (GameOver));q Q
((= keypress "W") (if (not (= direction "DOWN")) (setq direction "UP")));W
((= keypress "S") (if (not (= direction "UP")) (setq direction "DOWN")));S
((= keypress "A") (if (not (= direction "RIGHT")) (setq direction "LEFT")));A
((= keypress "D") (if (not (= direction "LEFT")) (setq direction "RIGHT")));D
);cond

(if (>= (/ (- (getvar "Millisecs") timer) 1000.0) tick)
(progn
(setq timer (getvar "Millisecs"))
(MoveSnake)
))
(foreach node snake (redraw node))
);main loop

(RemoveEnts)
(redraw)
(if (/= vars nil) (mapcar '(lambda (x)
(if (eval (read (car x))) (setvar (car x) (eval (read (car x)))))
(set (read (car x)) nil)) vars))
(setq *varlist* nil)
(gc)
))

(princ)
);defun

 

2 REPLIES 2
Message 2 of 3
dlanorh
in reply to: doaiena

You obviously have too much time on your hands! 😛

I am not one of the robots you're looking for

Message 3 of 3
doaiena
in reply to: dlanorh

The whole thing took around 4-5 hours. It was done within a single day.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report

”Boost