summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrant Shangreaux <grant@unabridgedsoftware.com>2022-02-06 20:52:29 -0600
committerGrant Shangreaux <grant@unabridgedsoftware.com>2022-02-06 20:52:29 -0600
commit3247b05b47a3fd8d4e6181fab2c9fb27a5f1c7cc (patch)
tree0d28056ade945bee86be67fcb185fe91ac0f5ca0
parentc3c1251235d094a4281ab6d2aef274ba553f4fac (diff)
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
-rw-r--r--mafia.org66
1 files 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"