From 49c62eae2a8b5a1c91a20be88b05632d275580d9 Mon Sep 17 00:00:00 2001 From: Grant Shangreaux Date: Sat, 27 Aug 2022 15:01:32 -0500 Subject: Add: much more Emacs literate code tutorial documentation --- wink-murder.org | 182 +++++++++++++++++++++++++++++++++++++++----------------- 1 file 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 ~<>~ 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 ~<>~, 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 ~<>~, 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. <> #+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 -- cgit v1.2.3