summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrant Shangreaux <grant@unabridgedsoftware.com>2022-08-27 15:01:32 -0500
committerGrant Shangreaux <grant@unabridgedsoftware.com>2022-08-27 15:01:32 -0500
commit49c62eae2a8b5a1c91a20be88b05632d275580d9 (patch)
treea9dab5e99f0e6a82bcd8711068c75f68b95f531e
parent644b87a848e7d5595c22a6294b0e940b80c9c55e (diff)
Add: much more Emacs literate code tutorial documentation
-rw-r--r--wink-murder.org182
1 files changed, 126 insertions, 56 deletions
diff --git a/wink-murder.org b/wink-murder.org
index e5c3230..15d3e96 100644
--- a/wink-murder.org
+++ b/wink-murder.org
@@ -3,9 +3,9 @@
This document serves as a tutorial for Emacs, Lisp, and org-mode's literate
programming features. It provides a way to distribute the notes, code, and
-execution environment all in one place. Anyone reading this document can also
-edit and execute the code in it. This is the beauty of literate programs, where
-the program is written for humans as prose with embedded code.
+execution environment all in one place. Anyone reading this document in Emacs
+can also edit and execute the code in it. This is the beauty of literate programs,
+where the program is written for humans as prose with embedded code.
Ideally I would like to create a small city/village simulator where there
are many independent actors all doing their routines. One of the actors gets
@@ -48,15 +48,20 @@ completion engine, it offers one way to discover what things are possible.
~org-mode~. Mostly, you will use it to execute blocks of code in this program.
* Program Overview
+** Main ~wink-murder~ Block
The following source block represents the whole program from a high level.
its like a table of contents, where each of the ~<<name>>~ blocks actually
-points to other source code blocks in this document. The header argument
-~:noweb yes~ tells ~org-mode~ to unfold the references into code, so when
-it sees ~<<dependencies>>~, it will insert the code from the block with
-that name. The header argument ~:tangle yes~ means we can evaluate
-~(org-babel-tangle)~ and it will unfold all of the code references into
-a source file. This is the core of literate programming.
+point to other source code blocks in this document.
+
+The header argument ~:noweb yes~ tells ~org-mode~ to unfold the references
+into code, so when it sees ~<<dependencies>>~, it will insert the code
+from the block with that name.
+
+The header argument ~:tangle yes~ means we can evaluate
+~(org-babel-tangle)~
+and it will unfold all of the code references into a source file. This
+is the core of literate programming.
#+name: wink-murder
#+begin_src emacs-lisp :noweb yes :tangle yes :results silent
@@ -77,45 +82,92 @@ a source file. This is the core of literate programming.
<<game-loop>>
#+end_src
-** Working with source blocks
+Read on, dear reader, to learn how to interact with these code blocks, and also
+how they interact with one another.
+
+** Working With Source Blocks
-If you put your cursor on the block above and press ~C-c C-c~, Emacs will
-ask if you want to evaluate the code on your system. This is a good thing,
-since you are executing most of the elisp in this file without looking
-at it! You can set a variable to allow it to execute automatically, but
-its nice to have the safety on. You can turn it off with ~C-c C-c~ on the
-block below:
+If you were to put your cursor on the ~wink-murder~ block above and press
+~C-c C-c~, Emacs will ask if you want to evaluate the code on your system.
+
+This is a good thing, since you would execute most of the elisp in this file
+without looking at it! Don't do it on the ~wink-murder~ block just yet,
+because I would like you to execute small parts of the program as you go.
+Any Elisp that is evaluated becomes part of the global Emacs session.
+Setting a variable or defining a function makes it available in the Lisp image
+running Emacs. Evaluate this ~hello-world~ block below with ~C-c C-c~
+and type "yes" when asked.
#+begin_src emacs-lisp
- (setq org-confirm-babel-evaluate nil)
+ (defun hello-world ()
+ (interactive)
+ (message "Hello world!"))
#+end_src
-** A little bit of Lisp
+You should see a =RESULTS= section that returns the result of the code block
+evaluation. Here, it says ~hello-world~ which is the symbol of the function
+we just defined.
-What does that expression up there do? Its basically the equivalent to
-~variable = value~ in Ruby or other languages. In Lisp, everything is an
-expression, symbols are important, and lists are surrounded with parens.
-When we evaluate Lisp, HERE-IS-WHERE-I-LEFT-OFF
-
-~setq~ actually stands
-for "set quote". ~quote~ is a special form in Lisp that says "don't evaluate
-the next expression, just return it". Var
+Now, press ~M-x~, type ~hello-world~ and press =RET=. You should see the
+message printed in the echo area down below. Your first Emacs command!
-Now you should be able to press M-x wink-murder-play and it will prompt you for
-a number of players. Enter 4 (lower) will. Then look at the *Messages*
-buffer, you can either find it through the menu bar, ~M-x switch-to-buffer~
-or C-c C-c the source block below:
+Its nice to have the safety of Emacs asking you if you want to execute the code
+on your machine, but... you can turn it off by setting a [[info:emacs#Easy Customization]["Custom" variable]]
+which controls the behavior. Press ~C-c C-c~ on the block below:
-#+begin_src emacs-lisp
-(switch-to-buffer "*Messages*")
+#+begin_src emacs-lisp :results silent
+ (setq org-confirm-babel-evaluate nil)
#+end_src
-** dependencies
+** A Little Bit of Lisp
-i'm going to bring in some dependencies that will make it more like Common
-Lisp. [[info:cl#Top][cl-lib]] brings in functions and macros. When you see things prefixed with
-~cl-~, you'll know its from Common Lisp. [[info:eieio#Top][EIEIO]] provides an OOP layer similar to
-CLOS, the powerful object oriented system from CL.
+What does that code block up there do? It is basically the equivalent to
+~variable = value~ in Ruby or other languages. In Lisp, everything is an
+expression, symbols are important, and lists are surrounded with parens.
+When Lisp is evaluated, the first element of a list is a function and the following
+elements are the arguments. So we're using the ~setq~ function (actually a it
+is a special form) to set the value of the ~org-confirm-babel-evaluate~
+symbol, and the value we're giving it is ~nil~.
+
+~setq~ actually stands for "set quote". ~quote~ is a special form in Lisp
+that says "don't evaluate the next expression, just return it". If you just pass in the
+unquoted symbol ~org-confirm-babel-evaluate~, it will be evaluated and
+its value returned. That isn't what you want here. Rather, you want to have the
+symbol itself, and set its value. That's where the ~quote~ part comes in. You could
+write the same expression as ~(set (quote some-symbol) some-value)~
+but since setting variables happens so often, the ~setq~ form was made for
+convienience. Its a special form because it /breaks the rules/ of typical Lisp evaluation.
+This is a key part of what makes Lisp so powerful, when you can create your own
+special forms (macros) that change the rules for a specific purpose.
+
+I'll also point out that since you ~quote~ expressions so often, there's a shorthand
+for it, unsuprisingly ~'~. If you see the single quote in front of /any/ expression it
+means "do not evaluate this, just return it as data". Let's try it in action:
+
+#+begin_src emacs-lisp :results output
+ (print 'foo) ;; print the symbol
+ (print '(a list of symbols)) ;; print the list
+ (print '(+ 5 5)) ;; what will this do?
+ (print (+ 5 5)) ;; how about this?
+ (print emacs-version) ;; this prints the value of the symbol
+ (print 'emacs-version) ;; with the quote, its just the symbol itself
+#+end_src
+
+See if you can predict what the block above will print, and then press ~C-c C-c~
+on it to see the results.
+
+** Requiring some Libraries
+
+I'm going to bring in some libraries that will allow us to write Elisp
+similar to Common Lisp. Elisp brings us an entire text based user interface
+for free. And we get to learn about Emacs itself. However, Common Lisp
+is a much more powerful and practical language to work with, and may be
+a good candidate for future simulations.
+
+The following source block is named ~dependencies~, which is the first noweb
+reference in the ~wink-murder~ block at the top of this section. Thus, these will
+be the first two things evaluated in the program. Go ahead and evaluate this block
+yourself before we move on to defining the game.
#+name: dependencies
#+begin_src emacs-lisp :results silent
@@ -123,21 +175,37 @@ CLOS, the powerful object oriented system from CL.
(require 'eieio)
#+end_src
+The [[info:cl#Top][cl-lib]] package brings in functions and macros from the Common Lisp language.
+When you see things prefixed with ~cl~, you'll know its from Common Lisp. One you
+may notice is the ~cl-loop~ macro, which is a whole little language unto itself.
+
+The [[info:eieio#Top][EIEIO]] library provides an OOP layer similar to
+CLOS, the powerful object oriented system from Common Lisp. The object style
+data structures are useful, and generic functions for subclass based specialization
+can be powerful and allow for easy extension.
+
* Game State
-a game consists of N >= 4 *actors*, one of which is the *killer*. each game will
-have a number of *rounds*, at the end of each an actor will /die/. each round
-must simulate time in some way. each *tick*, an actor /observes/ their
-fellows. the killer will /wink/ at another actor when they happen to be observing
-each other. after a *delay* of χ ticks, the winked-at actor dies and the round
-ends. once per round, each actor may /accuse/ another of being the killer.
-then the group attemps to reach /consensus/ on this accusation. if they do, the
-other actor reveals whether or not they are the killer. (Q: what makes the killer
-join the consensus against themself?) If they are the killer, the other actors
-win. If there's only 2 players left, the killer wins.
+A game consists of N >= 4 *actors*, one of which is the *killer*. Each
+game must simulate time in some way. Each *tick*, an actor /observes/ their
+fellows. The killer will /wink/ at another actor when they make eye-contact,
+which "kills" the winked-at actor.
+
+An actor may /accuse/ another of being the killer. Another actor must second
+the accusation to push it through. If they do, the other actor reveals
+whether or not they are the killer. If they are the killer, the other actors win.
+If they are not the killer, the game continues.
+
+All of these actions will be recorded as a list of *events* in a slot on the
+main game object. The events can represent the whole game, and also serve
+as triggers for other events, i.e. the killer wink event causes an actor
+death event.
+
+If there's only 2 players left, the killer wins.
** Game class ~wink-murder~
-using EIEIO, lets define a class ~wink-murder~ to represent the whole game state.
+
+Using EIEIO, we define a class ~wink-murder~ to represent the whole game state.
#+name: wink-murder-class
#+begin_src emacs-lisp :results silent
@@ -146,27 +214,29 @@ using EIEIO, lets define a class ~wink-murder~ to represent the whole game state
:initform (wink-murder-initialize-actors 4)
:type list
:documentation "The list of `actors' in the game.")
- (round :initform 1
- :type number
- :documentation "The current round of the game.")
(tick :initform 0
:type number
:documentation "The current 'slice-of-time' the game is in.")
(events :initform nil
:type list
:documentation "List of events in the game's timeline."))
- "A class representing the game state of Wink-Murder, defaults to 4 players.")q
+ "A class representing the game state of Wink-Murder, defaults to 4 players.")
#+end_src
Notice the ~:initform~ slot options (instance variables are called slots in
CLOS/EIEIO). By default, ~(make-instance 'wink-murder)~ would initialize the object
-instance with these values. We will work on defining ~wink-murder-initialize-actors~ next.
+instance with these values. We will define ~wink-murder-initialize-actors~ next.
** Actors
+:PROPERTIES:
+:header-args: :noweb-ref actor-classes :noweb-sep "\n\n" :results silent
+:END:
-We may as well use an object to represent the actors as well. Each one will have
-an *id* and a *status* which will be one of ~'alive 'dead 'killer~. We can actually
-use inheritance here to set apart the killer with its own ~:initform~.
+We can use an object to represent the actors as well. Each one will have
+an *id* and a *status* which will be one of ~'alive 'dying 'dead~. An actor
+also has a *target* which is the person they are currently observing. I
+added a *notes* slot as well, assuming we might want to record some of
+the "knowledge" an individual actor has about the others.
#+name: actor-classes
#+begin_src emacs-lisp :results silent