From 3247b05b47a3fd8d4e6181fab2c9fb27a5f1c7cc Mon Sep 17 00:00:00 2001 From: Grant Shangreaux Date: Sun, 6 Feb 2022 20:52:29 -0600 Subject: Add: events to game state and log them when game ends - list type events slot on mafia - helper methods for current game mafia-add-event & mafia-current-tick - update the tick in game loop - clear log buffer each new game - switch to buffer at game end --- mafia.org | 66 +++++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/mafia.org b/mafia.org index 3772e96..de5ba5f 100644 --- a/mafia.org +++ b/mafia.org @@ -11,6 +11,8 @@ the game [[https://en.wikipedia.org/wiki/Wink_murder]["Mafia"]] is a very simpli point to work out a prototype. for fun, i'm just doing this in emacs-lisp. the following source block represents the whole program from a high level: +** Program Overview + #+name: mafia #+begin_src emacs-lisp :noweb yes :tangle yes :results silent ;;; mafia.el --- Emacs Mafia game simulation -*- lexical-binding:t -*- @@ -90,8 +92,11 @@ using EIEIO, lets define a class ~mafia~ to represent the whole game state. :documentation "The current round of the game.") (tick :initform 0 :type number - :documentation "The current 'slice-of-time' the game is in.")) - "A class representing the game state of Mafia, defaults to 4 players.") + :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 Mafia, defaults to 4 players.")q #+end_src Notice the ~:initform~ slot options (instance variables are called slots in @@ -185,10 +190,12 @@ 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: ") + (with-current-buffer "*MAFIA-LOG*" (erase-buffer)) (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!")) + (mapc #'mafia-log-event (reverse (slot-value mafia-active-game 'events))) + (switch-to-buffer "*MAFIA-LOG*")) #+end_src * Top level game functions @@ -202,14 +209,22 @@ there is not reason to define methods a la Ruby. (defun mafia-living-innocents (game) "Returns the living innocents from a mafia game." (cl-remove-if-not #'mafia-alive-p (mafia-innocents game))) - + (defun mafia-update (game) "Performs the update logic for the mafia game instance." - (mapc 'mafia-observe (slot-value game 'actors))) - + (with-slots (actors tick) game + (setf tick (1+ tick)) + (mapc 'mafia-observe actors))) + (defun mafia-innocents (game) "Returns the list of innocents from a mafia game." (cl-remove-if #'mafia-killer-p (slot-value game 'actors))) + + (defun mafia-current-tick () + (slot-value mafia-active-game 'tick)) + + (defun mafia-add-event (event) + (object-add-to-list mafia-active-game 'events event)) #+end_src * Actor Behavior @@ -250,9 +265,13 @@ will never make eye contact. (cl-defmethod mafia-observe ((actor mafia-innocent)) (when (> 5 (random 11)) (with-slots (id target) actor - (let ((new-target (mafia-random-other actor (slot-value mafia-active-game 'actors)))) - (mafia-log "Innocent %d observes %d and sees they are %s" id - (mafia-actor-id new-target) (slot-value new-target 'status)) + (let* ((new-target (mafia-random-other actor (slot-value mafia-active-game 'actors))) + (new-id (mafia-actor-id new-target)) + (new-status (slot-value new-target 'status))) + (mafia-add-event + (mafia-retarget-event :actor-id id :old (mafia-actor-id target) :new new-id + :message (format "Innocent %d observes %d and sees they are %s" id + new-id new-status))) (setf target new-target)))) (cl-call-next-method)) #+end_src @@ -306,16 +325,21 @@ If they're being watched, simply have them target a random other actor (?) #+begin_src emacs-lisp :results silent (cl-defmethod mafia-observe ((killer mafia-killer)) "Specialized behavior for the `mafia-killer'." - (with-slots (target) killer - (if (mafia-being-watched? killer) - (let ((new-target (mafia-random-other killer (mafia-living-innocents mafia-active-game)))) - (mafia-log "the killer targets %d" (mafia-actor-id new-target)) - (setf target new-target)) - (if (mafia-eye-contact? killer target) + (with-slots (id target) killer + (with-slots ((old-id id)) target + (if (mafia-being-watched? killer) + (let* ((new-target (mafia-random-other killer (mafia-living-innocents mafia-active-game))) + (new-id (mafia-actor-id new-target))) + (mafia-add-event + (mafia-retarget-event :actor-id id :old old-id :new new-id + :message (format "the killer targets %d" new-id))) + (setf target new-target)) + (when (mafia-eye-contact? killer target) (progn - (mafia-log "the killer winks at %d." (mafia-actor-id target)) - (mafia-innocent-die target)))) - (cl-call-next-method killer))) + (mafia-add-event + (mafia-event :actor-id id :message (format "the killer winks at %d." old-id))) + (mafia-innocent-die target))) + (cl-call-next-method killer))))) #+end_src ** Selecting a random other actor @@ -443,7 +467,7 @@ its ~death-countown~, but I'm not quite prepared for that at this moment. #+begin_src emacs-lisp (defun mafia-innocent-die (actor) (with-slots (id status) actor - (mafia-log "Innocent %s has been killed!" id) + (mafia-add-event (mafia-event :actor-id id :message "AIIEEEE!!")) (setf status 'dead))) #+end_src @@ -479,7 +503,7 @@ something more specialized as needed. #+begin_src emacs-lisp (defclass mafia-event nil ((tick - :initarg :tick + :initform (mafia-current-tick) :custom number :label "Time of occurance" :documentation "The tick of the parent game when the event happened") @@ -544,7 +568,7 @@ to pass control to the base ~mafia-log-event~ method. #+begin_src emacs-lisp (mafia-log-event (mafia-retarget-event - :tick 389 :actor-id 3 :message "foo" :old 2 :new 8)) + :actor-id 3 :message "foo" :old 2 :new 8)) #+end_src * Emacs "UI" -- cgit v1.2.3