diff options
author | Grant Shangreaux <grant@unabridgedsoftware.com> | 2022-01-30 23:55:48 -0600 |
---|---|---|
committer | Grant Shangreaux <grant@unabridgedsoftware.com> | 2022-01-30 23:55:48 -0600 |
commit | 6dc6517af29e9b942b74a3b0216d1473324293c8 (patch) | |
tree | 08d876474b5646aee0cbdce2aa0b0e922adc9a5e | |
parent | 41122fa276762ea02838cf575c31fc21f7d84729 (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.org | 157 |
1 files changed, 119 insertions, 38 deletions
@@ -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 |