summaryrefslogtreecommitdiff
path: root/wink-murder.org
blob: 15d3e96888960fcc461244cf81e4ed6739b69a1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
* Building a Murder Mystery Sim Prototype With Emacs
** Summary

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 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
triggered by something and commits a murder. Then the player acts as detective,
talking to other actors and gaining clues to solve who-done-it.

** Wink Murder - boiling it down

The game [[https://en.wikipedia.org/wiki/Wink_murder]["Wink Murder"]] is
a simplified murder mystery and serves as a starting point to work out
a prototype. Actors observe one another while the killer tries to wink at
any innocent they make eye contact with before being accused and caught. This
will be a pure simulation, where we can learn about creating actor behavior
representing knowledge.

Does the killer think they're being watched?

Did one of the innocents notice them wink?

How can we encode some of these behaviors into a simulated version of people
sitting in a circle and playing this game?

** Org mode

If you're reading this in Emacs, you probably see headings with different colors
depending on their level. These headings can be folded by pressing TAB when the
cursor is on them. Navigation by heading is also possible, but we won't worry about
it for now. Notice in the section above, "Wink Murder" looks like a link. Click
it. Depending on what you have installed, you should land in a browser at the
Wikipedia page for the game. If you really like Emacs, you can even use ~eww~ as
the default browser to open links in. Keep an eye out for links in this program.
They can be links to nearly anything, like [[info:org#Top][this link to the org-mode info manual]].

I will ask you to learn just *two* keyboard shortcuts. Meta-x and Ctrl-c Ctrl-c.
(written ~M-x~ and ~C-c C-c~ after this). Meta will be your alt key or possibly
option, it depends on your machine and OS. ~M-x~ is for ~execute-extended-command~
which lets you run /any/ command avaliable in Emacs. Combined with a good
completion engine, it offers one way to discover what things are possible.
~C-c C-c~ does many things depending on context, in this case, specific to
~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 ~<<name>>~ blocks actually
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 ~<<dependencies>>~, 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
  ;;; wink-murder.el --- Emacs Wink-Murder game simulation  -*- lexical-binding:t -*-

  <<dependencies>>

  <<wink-murder-class>>

  <<actor-classes>>

  <<initialization-helpers>>

  <<top-level-game-functions>>

  <<actor-behavior-methods>>

  <<game-loop>>
#+end_src

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 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
  (defun hello-world ()
    (interactive)
    (message "Hello world!"))
#+end_src

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.

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!

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 :results silent
  (setq org-confirm-babel-evaluate nil)
#+end_src

** A Little Bit of Lisp

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
  (require 'cl-lib)
  (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 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, we define a class ~wink-murder~ to represent the whole game state.

#+name: wink-murder-class
#+begin_src emacs-lisp :results silent
  (defclass wink-murder () ; No superclasses
    ((actors :initarg :actors
             :initform (wink-murder-initialize-actors 4)
             :type list
             :documentation "The list of `actors' in 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.")
#+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 define ~wink-murder-initialize-actors~ next.

** Actors
:PROPERTIES:
:header-args: :noweb-ref actor-classes :noweb-sep "\n\n" :results silent
:END:

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
  (defclass wink-murder-actor ()
    ((id
      :initarg :id
      :type number
      :reader wink-murder-actor-id)
     (target
      :initarg :target
      :documentation "Actor currently being observed."
      :accessor wink-murder-actor-target)
     (status
      :initform 'alive
      :documentation "'alive, 'dying, or 'dead")
     (notes
      :initform '()
      :documentation "An alist containing pairs of (actor-id . target-id)"))
    "Base class for wink-murder actors.")
  
  (defclass wink-murder-killer (wink-murder-actor)
    ()
    "Actor subclass to represent the killer.")
  
  (defclass wink-murder-innocent (wink-murder-actor)
    ((death-countdown
      :initform (1+ (random 10))
      :documentation "The number of ticks to go from dying to dead")))
#+end_src

Then the function ~wink-murder-initial-actors~ will handle initializing N actors
into a list which is the ~:initform~ of the ~wink-murder~ game instance.

** Game Initialization Functions

#+name: initialization-helpers
#+begin_src emacs-lisp :results silent
  (defun wink-murder-initialize-actors (players)
    "Returns a list of `players' wink-murder-actors, where one is the killer."
    (cl-assert (>= players 4) () "Cannot play with fewer than 4 players")
    (let* ((killer (1+ (random players)))
           (actors (cl-loop for i from 1 to players
                            collect (if (eql i killer) (wink-murder-killer :id i) (wink-murder-innocent :id i)))))
      (mapc (lambda (a)
              (setf (wink-murder-actor-target a) (wink-murder-random-other a actors)))
            actors)))
#+end_src

** visualizing initial game state

Lets initialize a game with 6 actors and inspect it to see what to expect.

#+begin_src emacs-lisp
  (let* ((wink-murder-game (wink-murder :actors (wink-murder-initialize-actors 6)))
         (actors (slot-value wink-murder-game 'actors)))
    (cons '(class id status) ;; add the header row
          (mapcar (lambda (a)
                    (let ((class (eieio-object-class a)))
                      (with-slots (id status) a
                        (list class id (when (eql class 'wink-murder-innocent) status)))))
                  actors)))
#+end_src

* Basic Game Loop

The game loop will advance by a single tick where each actor /observes/ the others.
So... we need to define a game loop function, and a method for the actors to

#+name: game-loop
#+begin_src emacs-lisp :results silent
  (defun wink-murder-play (players)
    "Entry point to start a game of Wink-Murder."
    (interactive "nnumber of players: ")
    (with-current-buffer "*WINK-MURDER-LOG*" (erase-buffer))
    (setq wink-murder-active-game (wink-murder :actors (wink-murder-initialize-actors players)))
    (while (> (length (wink-murder-living-innocents wink-murder-active-game)) 1)
      (wink-murder-update wink-murder-active-game))
    (mapc #'wink-murder-log-event (reverse (slot-value wink-murder-active-game 'events)))
    (switch-to-buffer "*WINK-MURDER-LOG*"))
#+end_src

* Top level game functions

Now that i think of it, using functions here would be simpler. Its likely faster,
and is definitely in Common Lisp. Unless we want to dispatch or use method chains,
there is not reason to define methods a la Ruby.

#+name: top-level-wink-murder-functions
#+begin_src emacs-lisp :results silent
  (defun wink-murder-living-innocents (game)
    "Returns the living innocents from a wink-murder game."
    (cl-remove-if-not #'wink-murder-alive-p (wink-murder-innocents game)))
  
  (defun wink-murder-update (game)
    "Performs the update logic for the wink-murder game instance."
    (with-slots (actors tick) game
      (setf tick (1+ tick))
      (mapc 'wink-murder-observe actors)))
  
  (defun wink-murder-innocents (game)
    "Returns the list of innocents from a wink-murder game."
    (cl-remove-if #'wink-murder-killer-p (slot-value game 'actors)))
  
  (defun wink-murder-current-tick ()
    (slot-value wink-murder-active-game 'tick))
  
  (defun wink-murder-add-event (event)
    (object-add-to-list wink-murder-active-game 'events event))
#+end_src

* Actor Behavior
** Observation
:PROPERTIES:
:header-args: :noweb-ref actor-behavior-methods :noweb-sep "\n\n" :results silent
:END:

Each actor will have a ~target~, another actor they are observing.
While they are observing, they'll notice who their target is observing.
If two actors are observing each other, they have *eye-contact*. The killer
will wink if they believe they aren't ~being-watched?~ when eye contact
is being made.

We'll implement a base method for all actors that can be called after more
specialized methods. This base method will be responsible for the actors'
"memory", adding a note about who they're observing is observing. Perhaps
later on it will be "fuzzy."

#+name: observe-base
#+begin_src emacs-lisp :results silent
  (cl-defmethod wink-murder-observe ((actor wink-murder-actor))
    "Base behavior for an actor. Note the ids of the observed target and who
  they are perceived to be targeting."
    (with-slots (notes (my-target target)) actor
      (when my-target
        (with-slots (id (their-target target)) my-target
          (when their-target
            (setf notes (cons `(,id . ,(wink-murder-actor-id their-target)) notes)))))))
#+end_src

For now, we just need to give the innocents random chance to target a
new person. Otherwise, the sim will go into an endless loop as the killer
will never make eye contact.

#+name: observe-innocent
#+begin_src emacs-lisp
  (cl-defmethod wink-murder-observe ((actor wink-murder-innocent))
    (when (> 5 (random 11))
      (with-slots (id target) actor
        (let* ((new-target (wink-murder-random-other actor (slot-value wink-murder-active-game 'actors)))
               (new-id (wink-murder-actor-id new-target))
               (new-status (slot-value new-target 'status)))
          (wink-murder-add-event
           (wink-murder-retarget-event :actor-id id :old (wink-murder-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

We may not have to specialize the other actors, but while the killer is
observing, they will decide whether or not to wink. But first we'll need
a method to determine eye-contact and one for the killer to determine if
they're being watched.

#+name: eye-contact?
#+begin_src emacs-lisp
  (defun wink-murder-eye-contact? (a b)
    "Given two `wink-murder-actor's, returns t if they are eachother's current target."
    (and (equal (slot-value a 'target) b)
         (equal (slot-value b 'target) a)))
#+end_src

Eye contact is pretty straight forward, but ~being-watched?~ needs to utilize
the *notes* "memory" from above. For now, we'll look at the first element in
the list and see if the target is the killer.

#+name: neighbors
#+begin_src emacs-lisp
    (defun neighbors (e lst)
      (let* ((idx (cl-position e lst))
             (len (length lst))
             (left (elt lst (mod (1- idx) len)))
             (right (elt lst (mod (1+ idx) len))))

        (list left right)))
#+end_src

#+name: being-watched?
#+begin_src emacs-lisp
    (cl-defmethod wink-murder-being-watched? ((killer wink-murder-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 (?)

#+name: wink-murder-observe-killer
#+begin_src emacs-lisp :results silent
  (cl-defmethod wink-murder-observe ((killer wink-murder-killer))
    "Specialized behavior for the `wink-murder-killer'."
    (with-slots (id target) killer
      (with-slots ((old-id id)) target
        (if (wink-murder-being-watched? killer)
            (let* ((new-target (wink-murder-random-other killer (wink-murder-living-innocents wink-murder-active-game)))
                   (new-id (wink-murder-actor-id new-target)))
              (wink-murder-add-event
               (wink-murder-retarget-event :actor-id id :old old-id :new new-id
                                     :message (format "the killer targets %d" new-id)))
              (setf target new-target))
          (when (wink-murder-eye-contact? killer target)
            (progn
              (wink-murder-add-event
               (wink-murder-event :actor-id id :message (format "the killer winks at %d." old-id)))
              (wink-murder-innocent-die target)))
        (cl-call-next-method killer)))))
#+end_src

** Selecting a random other actor

#+name: wink-murder-random-other
#+begin_src emacs-lisp
  (defun wink-murder-random-other (actor other-actors)
    (with-slots (id) actor
      (let ((other-ids (cl-remove-if (lambda (i) (= i id))
                                     (mapcar 'wink-murder-actor-id other-actors))))
        (cdr (object-assoc (seq-random-elt other-ids) :id other-actors)))))
#+end_src

#+begin_src emacs-lisp
  (let* ((game (wink-murder :actors (wink-murder-initialize-actors 15)))
         (actor (cl-first (slot-value game 'actors))))
    ;; (cl-loop for i upto 10
             ;; collect (list (wink-murder-random-other actor (slot-value game 'actors))))
    (wink-murder-random-other actor (slot-value game 'actors))
    )
#+end_src

** TODO Example Three Actor Play
:PROPERTIES:
:header-args: :noweb-ref example-three-way-setup
:END:

*NOTE* code example here is currently "broken" due to random behavior in newer
code

Imagining "optimal" play if there are only 3 actors. The game begins and
each actor chooses a target. If the killer makes eye contact with anyone,
they'll wink, no matter if they're being observed or not, since they win
the game. If the other two make eye contact, they will never want to
observe the other player, because then they'll be killed. One of the two
would /accuse/ and the other would /second/ and they win.

Let's set this up. We'll need a killer with no target, and two innocents,
~marple~ the killer and ~poirot~ targets the her.

~let*~ sets some local variables for a block. latter definitions can refer
to variables created previously.

#+begin_src emacs-lisp
  (let* ((killer (wink-murder-killer :id 1))
         (marple (wink-murder-innocent :id 2 :target killer))
         (poirot (wink-murder-innocent :id 3 :target marple)))
#+end_src

Then, set the killer's target to ~poirot~ :

#+begin_src emacs-lisp
  (setf (slot-value killer 'target) poirot)
#+end_src

The killer observes:

#+begin_src emacs-lisp
  (wink-murder-observe killer)
#+end_src

Then change target and observe again:

#+begin_src emacs-lisp
  (setf (slot-value killer 'target) marple)
  (wink-murder-observe killer)) ;; end let*
#+end_src

~C-c C-c~ on the following block will run this code:

#+begin_src emacs-lisp :noweb yes :noweb-ref none :tangle no
  <<example-three-way-setup>>
  (with-current-buffer "*WINK-MURDER-LOG*" (buffer-string))
#+end_src

Add in a 4th actor, and then its trickier. The killer would like to wink
when they are sure they aren't being watched and then immediately try for
eye contact with another actor. The other actors may want to maintain
eye contact as long as they feel the actor they are observing is being
watched by someone else. ???

** Unfinished Targeting behavior

#+begin_src emacs-lisp

  (cl-defgeneric wink-murder-maybe-refocus (actor)
    "Actor decides to maintain observation target or pick another.")

  (cl-defmethod wink-murder-maybe-refocus ((actor wink-murder-actor) other-actors)
    (when (wink-murder-refocus? actor)
      (wink-murder-refocus actor other-actors)))

  (cl-defmethod wink-murder-refocus? ((actor wink-murder-killer))
    (slot-value actor 'being-watched?))

  (cl-defmethod wink-murder-refocus ((actor) other-actors)
    (with-slots ((target) actor)
        (setf target (seq-random-elt other-actors))))
#+end_src

** ~wink-murder-alive-p~ actor predicate
:PROPERTIES:
:header-args: :noweb-ref actor-behavior-methods :noweb-sep "\n\n" :results silent
:END:

#+begin_src emacs-lisp
  (defun wink-murder-alive-p (actor)
    "Returns `t' if the actor is alive, otherwise `nil'"
    (eql (slot-value actor 'status) 'alive))
#+end_src

** Dying Actors
:PROPERTIES:
:header-args: :noweb-ref actor-behavior-methods :noweb-sep "\n\n" :results silent
:END:

We need a method to make an actor die. For now, we'll just print some
message and update its state so that the ~alive~ slot is ~nil~. According
to the game rules, we should start some "timer" so that it will count down
its ~death-countown~, but I'm not quite prepared for that at this moment.

#+begin_src emacs-lisp
  (defun wink-murder-innocent-die (actor)
    (with-slots (id status) actor
      (wink-murder-add-event (wink-murder-event :actor-id id :message "AIIEEEE!!"))
      (setf status 'dead)))
#+end_src

* Events
** Summary

Rather than logging every single thing that happens in the sim, perhaps we can
emit Events when something significant happens. As devs wanting to inspec the
simulation, it might be nice to see every action one of the actors takes. Thus,
we can emit an event when an actor changes targets, but don't have to do anything
if they keep looking at the same one.

Its relatively the same to how the log function in the code (as i write this)
is only logging when the killer winks, someone dies, or target switches.

But this is data! It makes up the timeline of the simulation, and one list of
events can describe a whole game. It could be visualized by stepping through it
or perhaps showing it all laid out as one with certain events highlighted.

Giving some structure to this fact rather than just logging it into a buffer
gives us some more flexibility to displaying it down the road.

So we should ad a slot to ~wink-murder-game~ to be a list of ~wink-murder-event~ objects.
Each event could have a reference to the actor who caused it, the current ~tick~
or ~round~ when it happened (gotta clear up this time model), and some message
or other data. Specializing event types could allow us to use some generic
function like ~wink-murder-display-event~ and each type could use the base behavior or
something more specialized as needed.

** ~wink-murder-event~ classes

#+name: wink-murder-event-class
#+begin_src emacs-lisp
  (defclass wink-murder-event nil
    ((tick
      :initform (wink-murder-current-tick)
      :custom number
      :label "Time of occurance"
      :documentation "The tick of the parent game when the event happened")
     (actor-id
      :initarg :actor-id
      :custom number
      :label "Actor ID"
      :documentation "The id of the actor who caused event.")
     (message
      :initarg :message
      :custom string
      :label "Event message"
      :documentation "Freeform text string for an event message"))
    "Base class for events that happen during a wink-murder simulation.")
  
  (defclass wink-murder-retarget-event (wink-murder-event)
    ((old
      :initarg :old
      :custom number
      :label "ID of previous target")
     (new
      :initarg :new
      :custom number
      :label "ID of new target")))
#+end_src

#+RESULTS: wink-murder-event-class
: wink-murder-retarget-event

** ~wink-murder-event-log~ methods

The base method handles formatting the log string and sticking in all the relevant
data from the event object. The ~&rest extra~ in the argument list lets this method
take an unspecified number of optional extra parameters. We can pass down additional
pre-formatted strings from specialized methods and log all of those with
~(apply #'wink-murder-log extra-strings)~. The log string will look something like this
(subject to change):

  =000389: wink-murder-event actor 3 --- msg: pooop=

#+name: base-wink-murder-log-event
#+begin_src emacs-lisp
  (cl-defmethod wink-murder-log-event ((event wink-murder-event) &rest extra-strings)
    (with-slots (tick actor-id message) event
      (let ((format-string "%06d: %s actor %d --- msg: %s")
            (event-type (eieio-object-class event)))
        (wink-murder-log format-string tick event-type actor-id
                   (propertize message 'face 'font-lock-string-face))
        (when extra-strings (apply #'wink-murder-log extra-strings)))))
#+end_src

Lets specialize for the retarget event class, and we can ~cl-call-next-method~
to pass control to the base ~wink-murder-log-event~ method.

#+name: retarget-wink-murder-log-event
#+begin_src emacs-lisp
  (cl-defmethod wink-murder-log-event ((event wink-murder-retarget-event))
    (with-slots (actor-id old new) event
      (cl-call-next-method event (format "%02d focuses from %02d to %02d" actor-id old new))))
#+end_src

#+begin_src emacs-lisp
  (wink-murder-log-event
   (wink-murder-retarget-event
    :actor-id 3 :message "foo" :old 2 :new 8))
#+end_src

* Emacs "UI"

we can use an emacs buffer and all of the ways we have to manipulate text to
display the simulation. 

** ~wink-murder-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 wink-murder-log-buffer "*WINK-MURDER-LOG*"
    "Insert text here with the `wink-murder-log' function.")
#+end_src

borrowing a logging defun from [[info:emms#Top][EMMS]] :

#+begin_src emacs-lisp
  (defun wink-murder-log (&rest args)
    (with-current-buffer (get-buffer-create wink-murder-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.

** Inspecting/Manipulating Individual Actors

EIEIO has facilites to hook into the Emacs Custom / Widget apis if you add
correct properties to the class definition. For example:

#+begin_src emacs-lisp
  (require 'eieio-custom)
  
  (defclass my-foo nil
    ((a-string :initarg :a-string
               :initform "Thunderous pop!"
               :custom string
               :label "Amorphous String"
               :group (default foo)
               :documentation "A string for testing custom.
  This is the next line of documentation. It will be folded up
  in the 'UI'.")
     (listostuff :initarg :listostuff
                 :initform ("1" "2" "3")
                 :type list
                 :custom (repeat (string :tag "Stuff"))
                 :label "List of Strings"
                 :group foo
                 :documentation "A list of stuff."))
    "A class for testing the widget on.")
  
  (eieio-customize-object (my-foo))
#+end_src

This is Emacs specific, but can leverage the powerful text interface already provided.
You can extend the methods for editing and displaying the objects, so it could be used
while paused for inspecting and tweaking the state of anything in the simulation.

However, this and the log above make me think that we also need things we can detect as
the simulation runs. Something like ~wink-murder-event~ objects that build a *timeline* on the
game. The high level view would be focused on the timeline, rather than what an individual
actor is doing at any given time.