summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrant Shangreaux <grant@unabridgedsoftware.com>2022-01-30 23:55:48 -0600
committerGrant Shangreaux <grant@unabridgedsoftware.com>2022-01-30 23:55:48 -0600
commit6dc6517af29e9b942b74a3b0216d1473324293c8 (patch)
tree08d876474b5646aee0cbdce2aa0b0e922adc9a5e
parent41122fa276762ea02838cf575c31fc21f7d84729 (diff)
Add: mafia-observe into main loop and logging
- *MAFIA-LOG* buffer can be written to with `mafia-log` function - `mafia-update` maps over the actors with `mafia-observe` the simulation is poor, the killer randomly decides to wink and retarget, so the loop will end eventually. the actors don't try to do anything and behavior is nonsensical xD
-rw-r--r--mafia.org157
1 files changed, 119 insertions, 38 deletions
diff --git a/mafia.org b/mafia.org
index 06b7733..263a044 100644
--- a/mafia.org
+++ b/mafia.org
@@ -106,21 +106,29 @@ use inheritance here to set apart the killer with its own ~:initform~.
#+name: actor-classes
#+begin_src emacs-lisp :results silent
- (defclass mafia-actor ()
- ((id :initarg :id :type number)
- (target :initarg :target :documentation "Actor currently being observed.")
- (notes :initform '() :documentation "An alist containing pairs of (actor-id . target-id)"))
- "Base class for mafia actors.")
-
- (defclass mafia-killer (mafia-actor)
- ()
- "Actor subclass to represent the killer.")
-
- (defclass mafia-innocent (mafia-actor)
- ((status :initform 'alive
- :documentation "'alive, 'dying, or 'dead")
- (death-countdown :initform (1+ (random 10))
- :documentation "The number of ticks to go from dying to dead")))
+ (defclass mafia-actor ()
+ ((id
+ :initarg :id
+ :type number
+ :reader mafia-actor-id)
+ (target
+ :initarg :target
+ :documentation "Actor currently being observed."
+ :accessor mafia-actor-target)
+ (notes
+ :initform '()
+ :documentation "An alist containing pairs of (actor-id . target-id)"))
+ "Base class for mafia actors.")
+
+ (defclass mafia-killer (mafia-actor)
+ ()
+ "Actor subclass to represent the killer.")
+
+ (defclass mafia-innocent (mafia-actor)
+ ((status :initform 'alive
+ :documentation "'alive, 'dying, or 'dead")
+ (death-countdown :initform (1+ (random 10))
+ :documentation "The number of ticks to go from dying to dead")))
#+end_src
Then the function ~mafia-initial-actors~ will handle initializing N actors
@@ -133,9 +141,12 @@ into a list which is the ~:initform~ of the ~mafia~ game instance.
(defun mafia-initialize-actors (players)
"Returns a list of `players' mafia-actors, where one is the killer."
(cl-assert (>= players 4) () "Cannot play with fewer than 4 players")
- (let ((killer (1+ (random players))))
- (cl-loop for i from 1 to players
- collect (if (eql i killer) (mafia-killer :id i) (mafia-innocent :id i)))))
+ (let* ((killer (1+ (random players)))
+ (actors (cl-loop for i from 1 to players
+ collect (if (eql i killer) (mafia-killer :id i) (mafia-innocent :id i)))))
+ (mapc (lambda (a)
+ (setf (mafia-actor-target a) (mafia-random-other a actors)))
+ actors)))
#+end_src
*** visualizing initial game state
@@ -159,8 +170,8 @@ Lets initialize a game with 6 actors and inspect it to see what to expect.
| mafia-innocent | 2 | alive |
| mafia-innocent | 3 | alive |
| mafia-innocent | 4 | alive |
-| mafia-innocent | 5 | alive |
-| mafia-killer | 6 | nil |
+| mafia-killer | 5 | nil |
+| mafia-innocent | 6 | alive |
** Basic Game Loop
@@ -172,10 +183,10 @@ So... we need to define a game loop function, and a method for the actors to
(defun mafia-play (players)
"Entry point to start a game of Mafia."
(interactive "nnumber of players: ")
- (let* ((game (mafia :actors (mafia-initialize-actors players))))
- (while (> (length (mafia-living-innocents game)) 1)
- (mafia-update game))
- (message "Game over!")))
+ (setq mafia-active-game (mafia :actors (mafia-initialize-actors players)))
+ (while (> (length (mafia-living-innocents mafia-active-game)) 1)
+ (mafia-update mafia-active-game))
+ (message "Game over!"))
#+end_src
** Top level game functions
@@ -192,7 +203,7 @@ there is not reason to define methods a la Ruby.
(defun mafia-update (game)
"Performs the update logic for the mafia game instance."
- (mafia-innocent-die (cl-first (mafia-living-innocents game))))
+ (mapc 'mafia-observe (slot-value game 'actors)))
(defun mafia-innocents (game)
"Returns the list of innocents from a mafia game."
@@ -225,7 +236,7 @@ later on it will be "fuzzy."
(when my-target
(with-slots (id (their-target target)) my-target
(when their-target
- (setf notes (cons `(,id . ,(slot-value their-target 'id)) notes)))))))
+ (setf notes (cons `(,id . ,(mafia-actor-id their-target)) notes)))))))
#+end_src
We may not have to specialize the other actors, but while the killer is
@@ -258,13 +269,17 @@ the list and see if the target is the killer.
#+name: being-watched?
#+begin_src emacs-lisp
- (cl-defmethod mafia-being-watched? ((killer mafia-killer))
- "Specilized on the killer, returns true when there is a most recent memory,
-and the target is the killer themselves."
- (with-slots (id notes) killer
- (let* ((most-recent-memory (car notes))
- (their-target (cdr most-recent-memory)))
- (and their-target (= id their-target)))))
+ (cl-defmethod mafia-being-watched? ((killer mafia-killer))
+ "Specilized on the killer, returns true when there is a most recent memory,
+ and the target is the killer themselves."
+
+ ;; (with-slots (id notes) killer
+ ;; (let* ((most-recent-memory (car notes))
+ ;; (their-target (cdr most-recent-memory)))
+ ;; (and their-target (= id their-target))))
+
+ (> 5 (random 11))
+ )
#+end_src
If they're being watched, simply have them target a random other actor (?)
@@ -275,14 +290,39 @@ If they're being watched, simply have them target a random other actor (?)
"Specialized behavior for the `mafia-killer'."
(with-slots (target) killer
(if (mafia-being-watched? killer)
- (setf target (mafia-random-other killer))
+ (let ((new-target (mafia-random-other killer (slot-value mafia-active-game 'actors))))
+ (mafia-log "the killer targets %d" (mafia-actor-id new-target))
+ (setf target new-target))
(if (mafia-eye-contact? killer target)
- (print "the killer winks")
- (print "the killer abides"))
- ;; (mafia-wink other)
- (cl-call-next-method killer))))
+ (progn
+ (mafia-log "the killer winks at %d." (mafia-actor-id target))
+ (mafia-innocent-die target))
+ (mafia-log "the killer abides")))
+ (cl-call-next-method killer)))
#+end_src
+*** Selecting a random other actor
+
+#+name: mafia-random-other
+#+begin_src emacs-lisp
+ (defun mafia-random-other (actor other-actors)
+ (with-slots (id) actor
+ (let ((other-ids (cl-remove-if (lambda (i) (= i id)) (mapcar 'mafia-actor-id other-actors))))
+ (cdr (object-assoc (seq-random-elt other-ids) :id other-actors)))))
+#+end_src
+
+#+begin_src emacs-lisp
+ (let* ((game (mafia :actors (mafia-initialize-actors 15)))
+ (actor (cl-first (slot-value game 'actors))))
+ ;; (cl-loop for i upto 10
+ ;; collect (list (mafia-random-other actor (slot-value game 'actors))))
+ (mafia-random-other actor (slot-value game 'actors))
+ )
+#+end_src
+
+#+RESULTS:
+: #s(mafia-killer 15 #s(mafia-innocent 5 #s(mafia-innocent 12 #1 nil alive 5) nil alive 7) nil)
+
*** Example Three Actor Play
:PROPERTIES:
:header-args: :noweb-ref example-three-way-setup
@@ -391,6 +431,47 @@ its ~death-countown~, but I'm not quite prepared for that at this moment.
(setf status 'dead)))
#+end_src
+** Emacs "UI"
+
+we can use an emacs buffer and all of the ways we have to manipulate text to
+display the simulation.
+
+*** ~mafia-log~
+
+For now, we'll just make a log buffer so we can print "debug" messages
+to it as the game progresses.
+
+#+begin_src emacs-lisp
+ (defvar mafia-log-buffer "*MAFIA-LOG*"
+ "Insert text here with the `mafia-log' function.")
+#+end_src
+
+borrowing a logging defun from [[info:emms#Top][EMMS]] :
+
+#+begin_src emacs-lisp
+ (defun mafia-log (&rest args)
+ (with-current-buffer (get-buffer-create mafia-log-buffer)
+ (goto-char (point-max))
+ (insert (apply #'format args) "\n")))
+#+end_src
+
+~with-current-buffer~ temporarily sets the "current buffer" for all basic text
+operations. here we're using the buffer variable declared above, going to the
+end of it with ~point-max~ and inserting whatever ~(apply #'format args)~ is.
+
+Let's see...
+
+#+begin_src emacs-lisp
+ (apply #'format (list "foo %s %s" "bar" "baz"))
+#+end_src
+
+#+RESULTS:
+: foo bar baz
+
+Ah ok, we can pack up data for a ~format~ string into a list and print whatever.
+
+
+
* english villiage simulator
https://archaeology.co.uk/articles/specials/timeline/the-origins-of-the-english-village.htm